博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Ant Design源码分析(三):Wave组件
阅读量:6473 次
发布时间:2019-06-23

本文共 5640 字,大约阅读时间需要 18 分钟。

Wave组件效果预览

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

Button组件

Wave提供的动画效果

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组件的运行逻辑可以通过下方这张图来概括

图片描述

本篇完~

转载地址:http://klvko.baihongyu.com/

你可能感兴趣的文章
为微信小程序增加mixin扩展
查看>>
JavaScript专题之jQuery通用遍历方法each的实现
查看>>
svg简单的小案例
查看>>
「翻译」新增自订义工具列及按钮
查看>>
spring security运行时配置ignore url
查看>>
用Python实现一个优先级队列(Priority Queue)
查看>>
给自己的Fonts教程续
查看>>
剖析 Laravel 计划任务--创建和运行系统命令
查看>>
让拆库拆表见鬼去吧! MySQL 扩展新玩法
查看>>
Javascript面向对象编程 -- 设计模式
查看>>
用Python多线程实现生产者消费者模式
查看>>
PHP执行外部程序的方法
查看>>
利用react-redux-tpl快速开发react-redux-webpack项目
查看>>
Linux_《Linux命令行与shell脚本编程大全》第一章学习总结
查看>>
CI/CD:DevOps背后的推动力
查看>>
css布局:块级元素垂直居中
查看>>
深入JVM彻底剖析ygc越来越慢的原因(下)
查看>>
小米松果电子拆分成立大鱼半导体,专注IoT芯片研发
查看>>
架构之重构的12条军规(上)
查看>>
<转载>如何创建属于你自己的域名邮箱
查看>>