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.resetEffect
与 this.onClick
1、过滤不实行的前提(代码省略掉了,非骨干逻辑)
2、声明onClick
函数,并作为node
的点击事宜触发时要实行的函数
3、返回一个贮存了作废监听click
事宜要领的对象
个人认为bindAnimationEvent
要领,做了太多的事变,而ComponentDidMount
做的事变太少(单一准绳)
往下方,继承检察 this.resetEffect
与 this.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);
}
}
组件逻辑:
我们回过头来看第一部份的代码,组件逻辑体现在componentDidMount
与 componentWillUnMount
中
1、在componentDidMount
中为this.props.children
的click
事宜绑定动画实行函数this.onClick
,
2、在componentWillUnMount
中消灭与动画相干的状况防止内存走漏。
组件运转逻辑:
此时,Wave
组件的代码与逻辑已悉数剖析完了,全部Wave组件的运转逻辑能够经由过程下方这张图来归纳综合
本篇完~