资讯专栏INFORMATION COLUMN

Ant Design源码分析(三):Wave组件

luzhuqun / 3587人阅读

摘要:返回删除的节点。组件运行逻辑此时,组件的代码与逻辑已经全部分析完了,整个组件的运行逻辑可以通过下方这张图来概括本篇完

Wave组件效果预览

       在上一篇文章Button组件的源码分析中遇到了一个Wave组件, Wave组件在Ant design中提供了通用的表单控件点击效果,在自己阅读源码之前,也并没有过更多留心过在这些表单控件的动画效果是如何实现的,甚至可能有时都没注意到这些动画效果。下面先一起来看以下具体的效果(留意边框以外,一闪一闪的波浪动画效果):

Button组件

Radio组件

Switch组件

       看完UI效果之后我们大概已经知道是什么了,再看代码部分,由于代码书写顺序与阅读顺序并不一致,为了方便理解,我们在分析源码的过程中,会调整代码解释的顺序

源码分析
// 一个新的依赖,暂时不知道是什么,依据名字推测与动画效果有关
import TransitionEvents from "css-animation/lib/Event";

export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
  
  //... some type code
  
  // 我们发现Wave组件只提供组件逻辑,不参与UI展示,这种容器组件,往往在DidMount或WillMount声明周期中开始
  // 构建组件逻辑,往下看
  render() {
    return this.props.children;
  }
  
  // 只有一行代码, 先看下方bindAnimationEvent方法
  componentDidMount() {
    this.instance = this.bindAnimationEvent(findDOMNode(this) as HTMLElement);
  }
    
  // 在组件卸载时,清除掉事件监听与定时器,避免内存泄漏
  componentWillUnmount() {
    if (this.instance) {
      this.instance.cancel();
    }
    if (this.clickWaveTimeoutId) {
      clearTimeout(this.clickWaveTimeoutId);
    }
  }  

  // 根据名字推测: 为DOM节点绑定动画效果,进入函数内部查看
  bindAnimationEvent = (node: HTMLElement) => {
   //... some code 

    const onClick = (e: MouseEvent) => {
      //... some code

      // 无论是否正在执行动画,先清除掉动画效果(至于怎么清除,先不关注)
      this.resetEffect(node);
      
      // 从target取得颜色值
      const waveColor =
        getComputedStyle(node).getPropertyValue("border-top-color") || // Firefox Compatible
        getComputedStyle(node).getPropertyValue("border-color") ||
        getComputedStyle(node).getPropertyValue("background-color");

      // 在这里可以看到之前定义的私有变量clickWaveTimeoutId,被用作储存一个定时器
      this.clickWaveTimeoutId = window.setTimeout(() => this.onClick(node, waveColor), 0);
    };
  
    // 监听node(this.props.children)的onClick事件
    node.addEventListener("click", onClick, true);

    // 将移除监听事件的回调函数封装在一个对象中并作为返回值,看着这里应该想起之前定义的私有变量instance,
    // 回顾DidMount生命周期函数,你会发现这个返回的对象将会被储存在instance中
    return {
      cancel: () => {
        node.removeEventListener("click", onClick, true);
      },
    };
  }

  //未完待续

我们通过观察上方bindAnimationEvent方法,其主要做了三件事,调用了两个外部函数this.resetEffectthis.onClick
1、过滤不执行的条件(代码省略掉了,非主干逻辑)
2、声明onClick函数,并作为node的点击事件触发时要执行的函数
3、返回一个储存了取消监听click事件方法的对象

个人认为bindAnimationEvent方法,做了太多的事情,而ComponentDidMount做的事情太少(单一原则)
往下方,继续查看 this.resetEffectthis.onClick 分别是做什么的,以及如何实现的

  // 这个函数是实现动画效果的核心,其主要有三个行为:1、创建内联style标签, 2、插入css字符串 3、并将其插入到document中
  // 我们知道css也是可以控制DOM变化的,比如伪类元素:after :before  这里正是通过:after来实现效果的
  onClick = (node: HTMLElement, waveColor: string) => {
    //... some code 1
    const { insertExtraNode } = this.props;

    /* 创建了一个div元素extraNode,装饰该div,在inserExtracNode= true时,将extraNode作为node的子元素 */
    /* 创建一个div元素,并缓存中私有变量extraNode中 */
    this.extraNode = document.createElement("div");
    
    /* 这里用let 更合适 */
    const extraNode = this.extraNode;
    extraNode.className = "ant-click-animating-node";
    // 可能有人好奇这个extraNode是干嘛的?
    // 往之后的代码中看的时候会发现,在insertExtraNode为false时,Wave通过插入伪类元素:after 来作为承载动画效果的DOM元素
    // 在insertExtraNode = true时,会生成一个div替代:after伪类元素,猜测是某些this.props.children可能自带:after,所以
    // 使用div元素来替代:after避免冲突,在这里我们只需要知道它是作为承载动画css的DOM元素即可
   
    // 获取指定的string insertExtraNode ? "ant-click-animating" : "ant-click-animating-without-extra-node";
    const attributeName = this.getAttributeName();  
    
    // Element.removeAttribute("someString");  从element中删除值为someString的属性
    // Element.setAttribute(name, value); 为element元素值为value的name属性
    node.removeAttribute(attributeName);
    node.setAttribute(attributeName, "true");
    
    // 行为1:这里创建了一个内联style标签
    styleForPesudo = styleForPesudo || document.createElement("style");
    if (waveColor &&
        waveColor !== "#ffffff" &&
        waveColor !== "rgb(255, 255, 255)" &&
        this.isNotGrey(waveColor) &&
        /* 透明度不为0的任意颜色 */
        !/rgba(d*, d*, d*, 0)/.test(waveColor) &&  // any transparent rgba color
        waveColor !== "transparent") {
          /* 给子元素加上borderColor */
        extraNode.style.borderColor = waveColor;
        
        /* 行为2:在内联style标签中插入样式字符串, 利用伪元素:after作为承载效果的DOM */
        styleForPesudo.innerHTML =
            `[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
            
        /* 行为3:将style标签插入到document中 */
      if (!document.body.contains(styleForPesudo)) {
        document.body.appendChild(styleForPesudo);
      }
    }    

     /* 在inserExtarNode为true时,将extraNode插入到node子元素中 */
    if (insertExtraNode) {
      node.appendChild(extraNode);
    }
    
    /* 为元素增加动画效果 */
    TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
  }

  /**
   * 重置效果
   * 顾名思义:这个函数通过三个行为,致力于一件事情,取消动画效果
   * 1、删除node的attribute属性 2、node的子元素 3、删除对应的内联style标签
   */
  resetEffect(node: HTMLElement) {
    // come code ...
    
    const { insertExtraNode } = this.props;
    
    const attributeName = this.getAttributeName();
    /* 行为1:删除node的attribute属性 */
    node.removeAttribute(attributeName);

    /* 行为3: 清空了为伪类元素内置的style标签 styleForPesudo */
    this.removeExtraStyleNode();

    if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
      // Node.removeChild() 方法从DOM中删除一个子节点。返回删除的节点。
      node.removeChild(this.extraNode);
    }
    TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
  }

  // 删除内联style标签
  removeExtraStyleNode() {
    if (styleForPesudo) {
      styleForPesudo.innerHTML = "";
    }
  }
      
  // 在每次动画执行结束后,清除掉状态,完成一个生命周期
  onTransitionEnd = (e: AnimationEvent) => {
    // todo
    if (!e || e.animationName !== "fadeEffect") {
      return;
    }

    this.resetEffect(e.target as HTMLElement);
  }

}
组件逻辑:

我们回过头来看第一部分的代码,组件逻辑体现在componentDidMountcomponentWillUnMount
1、在componentDidMount中为this.props.childrenclick事件绑定动画执行函数this.onClick
2、在componentWillUnMount中清除与动画相关的状态避免内存泄漏。

组件运行逻辑:

此时,Wave组件的代码与逻辑已经全部分析完了,整个Wave组件的运行逻辑可以通过下方这张图来概括

本篇完~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102079.html

相关文章

  • Ant design的Notification源码分析

    摘要:通过将实例传入回调函数。添加再回过头来看回调函数的内容。其中的作用是一次调用传入的各函数,其中方法是移除中相应的节点,是传入的关闭标签后的回调函数。 notification简介 showImg(https://segmentfault.com/img/remote/1460000014117558?w=483&h=135); notification就是通知提醒框,在系统四个角显示通...

    SimpleTriangle 评论0 收藏0
  • Ant-Design-组件-——-Form表单(一)

    摘要:擅长网站建设微信公众号开发微信小程序开发小游戏制作企业微信制作建设,专注于前端框架服务端渲染技术交互设计图像绘制数据分析等研究。 Ant Design of React @3.10.9 拉取项目 luwei.web.study-ant-design-pro, 切换至 query 分支,可看到 Form 表单实现效果 实现一个查询表单 showImg(https://segmentfau...

    trilever 评论0 收藏0
  • 前端最实用书签(持续更新)

    摘要:前言一直混迹社区突然发现自己收藏了不少好文但是管理起来有点混乱所以将前端主流技术做了一个书签整理不求最多最全但求最实用。 前言 一直混迹社区,突然发现自己收藏了不少好文但是管理起来有点混乱; 所以将前端主流技术做了一个书签整理,不求最多最全,但求最实用。 书签源码 书签导入浏览器效果截图showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 评论0 收藏0
  • TypeScript 、React、 Redux和Ant-Design的最佳实践

    摘要:使用官方的的另外一种版本和一起使用自动配置了一个项目支持。需要的依赖都在文件中。带静态类型检验,现在的第三方包基本上源码都是,方便查看调试。大型项目首选和结合,代码调试维护起来极其方便。 showImg(https://segmentfault.com/img/bVbrTKz?w=1400&h=930); 阿特伍德定律,指的是any application that can be wr...

    wangbinke 评论0 收藏0

发表评论

0条评论

luzhuqun

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<