当前位置: 首页 > Web前端 > HTML5

AntDesign源码分析(三):Wave组件

时间:2023-04-05 11:01:45 HTML5

Wave组件效果预览上一篇在Button组件的源码分析中,遇到了一个Wave组件。Wave组件提供了Ant设计中通用的表单控件点击效果。在自己阅读源码之前,并没有过多关注这些窗体控件的动画效果是如何实现的,有时甚至可能没有注意到这些动画效果。下面我们一起来看看具体的效果(注意边框外闪烁的波浪动画效果):Button组件Radio组件Switch组件看完UI效果我们大概已经知道是什么了,再看代码部分,因为代码写的顺序和读的顺序不一致。为了方便理解,我们在分析源码的过程中会调整代码解释的顺序源码分析//一个新的依赖,暂时不知道是什么,看名字就知道了推测与动画效果有关importTransitionEventsfrom'css-animation/lib/Event';exportdefaultclassWaveextendsReact.Component<{insertExtraNode?:boolean}>{//...sometypecode//我们发现Wave组件只提供组件逻辑,不参与UIShow,这种容器组件,往往在DidMount或WillMount语句循环中启动//构建组件逻辑,往下看render(){returnthis.props。孩子们;}//只有一行代码,先看bindAnimationEvent方法componentDidMount(){this.instance=this.bindAnimationEvent(findDOMNode(this)asHTMLElement);}//当组件被卸载时,清除事件监听器和定时器以避免内存泄漏componentWillUnmount(){if(this.instance){this.实例.取消();}if(this.clickWaveTimeoutId){clearTimeout(this.clickWaveTimeoutId);}}//从名字猜:给DOM节点绑定动画效果,进入函数查看bindAnimationEvent=(node:HTMLElement)=>{//...somecodeconstonClick=(e:MouseEvent)=>{//...一些代码//不管动画是否正在执行,先清除动画效果(至于怎么清除,不用关注)this.resetEffect(node);//从目标获取颜色值constwaveColor=getComputedStyle(node).getPropertyValue('border-top-color')||//Firefox兼容getComputedStyle(node).getPropertyValue('border-color')||getComputedStyle(node).getPropertyValue('背景色');//这里可以看到前面定义的私有变量clickWaveTimeoutId是用来存放一个定时器的this.clickWaveTimeoutId=window.setTimeout(()=>this.onClick(node,waveColor),0);};//监听节点(this.props.children)的onClick事件node.addEventListener('click',onClick,true);//将移除监听事件的回调函数封装在一个对象中作为返回值。看到这里,应该会想到之前定义的私有变量instance,//回过头来看DidMount生命周期函数,会发现返回的对象会存放在instance中return{cancel:()=>{node.removeEventListener('点击',onClick,true);},};}//未完待续我们观察上面的bindAnimationEvent方法,主要做了三件事,调用了两个外部函数this.resetEffect和this.onClick1.过滤非执行条件(代码省略,非主干逻辑)2.声明onClick函数,作为节点的点击事件触发时要执行的函数3.返回一个存储取消点击事件方法的对象)往下看,继续看this.resetEffect和this.onClick分别做了什么,以及如何实现//这个函数是实现动画效果的核心,??主要有三个行为:1.创建内联样式标签,2.插入css字符串3,插入到文档中//我们知道css也可以控制DOM变化,比如伪类元素:after:before这里是onClick=(node:HTMLElement,waveColor:string)=>{//...somecode1const{insertExtraNode}=this.道具;/*创建一个div元素extraNode,对div进行装饰,当insertExtracNode=true时将extraNode作为node的子元素*//*创建一个div元素并缓存在私有变量extraNode中*/this.extraNode=document.createElement('股利');/*这里用let比较合适*/constextraNode=this.extraNode;extraNode.className='蚂蚁点击动画节点';//可能有人会疑惑这个extraNode是做什么用的?//后面看代码会发现,当insertExtraNode为false时,Wave会插入一个伪类元素:after作为携带动画效果的DOM元素//当insertExtraNode=true时,会生成一个div代替of:after伪类元素,猜测是有些this.props.children可能有自己的:after,所以//使用div元素代替:after避免冲突,这里我们只需要知道是可以作为携带动画的DOM元素css//获取指定字符串insertExtraNode?'ant-click-animating':'ant-click-animating-without-extra-node';constattributeName=this.getAttributeName();//Element.removeAttribute('someString');从元素中移除值为someString的属性//Element.setAttribute(name,value);元素元素值的name属性node.removeAttribute(attributeName)为valuenode.setAttribute(attributeName,'true');//行为1:这里创建了一个内联样式标签styleForPesudo=styleForPesudo||document.createElement('样式');if(waveColor&&waveColor!=='#ffffff'&&waveColor!=='rgb(255,255,255)'&&this.isNotGrey(waveColor)&&/*透明度不为0的任何颜色*/!/rgba\(\d*,\d*,\d*,0\)/.test(waveColor)&&//任何透明的rgba颜色waveColor!=='transparent'){/*给子元素添加borderColor*/extraNode.style.borderColor=waveColor;/*行为2:内联样式标签插入样式字符串,使用伪元素:aft呃作为携带效果的DOM*/styleForPesudo.innerHTML=`[ant-click-animating-without-extra-node]:after{border-color:${waveColor};}`;/*行为3:将样式标签插入文档*/if(!document.body.contains(styleForPesudo)){document.body.appendChild(styleForPesudo);}}/*当insertExtarNode为真时,将extraNode插入到节点子元素中*/if(insertExtraNode){node.appendChild(extraNode);}/*给元素添加动画效果*/TransitionEvents.addEndEventListener(node,this.onTransitionEnd);/***Reseteffects*顾名思义:该函数专门用于Onething,取消动画效果*1.删除节点2的attribute属性,节点3的子元素,删除对应的内联样式标签*/resetEffect(node:HTMLElement){//来代码...const{insertExtraNode}=this.props;constattributeName=this.getAttributeName();/*行为1:删除节点的attribute属性*/node.removeAttribute(attributeName);/*行为3:清除伪类元素的内置样式标签styleForPesudo*/this.removeExtraStyleNode();如果(插入额外节点e&&this.extraNode&&node.contains(this.extraNode)){//Node.removeChild()方法从DOM中删除一个子节点并返回删除的节点。node.removeChild(this.extraNode);}TransitionEvents.removeEndEventListener(node,this.onTransitionEnd);}//删除内联样式标签removeExtraStyleNode(){if(styleForPesudo){styleForPesudo.innerHTML='';}}//在每次动画执行后,清除状态并完成一个生命周期onTransitionEnd=(e:AnimationEvent)=>{//todoif(!e||e.animationName!=='fadeEffect'){return;}这。resetEffect(e.targetasHTMLElement);}}组件逻辑:我们回头看第一部分代码,组件逻辑体现在componentDidMount和componentWillUnMount1.在componentDidMount中,为this.props.children函数this.onClick的点击事件绑定动画执行,2。清除componentWillUnMount中的动画相关状态,避免内存泄漏。组件运行逻辑:至此,Wave组件的代码和逻辑已经全部分析完毕,整个Wave组件的运行逻辑可以用下图来概括。