精通Vue(16):transition实现原理(二)回调
什么是回调?
回调就是指动画执行前、后留给用户的一个处理机会,看它的运行时机比较好理解一些:

从图示中可以看到有三个回调。
第一个回调:before-enter
它发生在动画dom安装class之前。那么vue就会喊一下,我要准备安装缓动class了,要不要处理一下?可以看到实参是el元素,在这里我们可以设置el的一些初始状态,可以想像是生产之前的准备工作。
第二个回调:enter
发生在一切准备就绪,且下一帧渲染之前的地方。 Vue会喊一下,我们马上就要开始执行动画了,要不要处理一下?
这个地方比较重大,由于它会同时将cb传进来,是什么意思呢?
cb是Vue内部做的一个动画结束处理回调,简单说就是,如果用户什么都没说,Vue会在内部生成一个默认的回调,这个回调就处理动画结束之后的善后工作,列如清理class等,在许多情况下我们是偷懒的,所以直接默认用它的。
但有时我们需要自己掌控一些细节,也就是说有些特殊场景Vue不能帮我们做。
好吧,这里就给用户留一个接口,你要不要处理一下?如果我们用了两个形参,那么Vue就认为我们想自定义动画结束的处理,但是它为什么要把cb也一块传过来呢?
由于这个cb是托底的。打个比方说,我们先自己处理,处理完成之后再调用Vue的cb,让它帮我们做一些善后工作,我们就轻松许多。假定没有这个cb,又要重新写一次,模型如下:
enter(el,cb){
//自定义结束回调
el.addEventListener('transitionend',function (){
//做我们自己的业务...
el.style.border='1px solid blue'
el.innerText='动画结束了!'
//执行Vue的默认回调,(可选)...
cb()
})
},
分析:在这个代码中,我们完全掌控了动画结束之后的处理,通俗说就是这个事不用Vue来了我自己做。因此在这里自定义了一个transitionend的回调,在内部做我们的任务。在最后我们再调用Vue的默认回调,这样省了许多事。
可以看出,这是精细控制动画结束的一个重大时机!
另外需要提的是,浏览器渲染是异步的,列如说我们把class加到一个div上是不会马上生效的,由于下一帧的渲染之前必须清空当前线程栈,也就是当前主线程必须全部执行完成。而我们看到enter还在主线程中执行中,所以就轮不到下一次渲染,这就是enter的运行时机。
第三个回调:after-enter
缓动结束之后,也就是transitionend的善后处理工作完成之后,就执行after-enter回调。这说明动画已经彻底完成了。
正常流程下,它是在Vue内部的cb中执行的。如果我们自定义了回调且没有调用Vue的cb,那么这个after-enter不会执行。
完整的实验代码
<style>
.box {
width: 200px;
height: 200px;
text-align: center;
line-height: 100px;
margin: 20px;
}
.my-appear-class {
transform: translateX(100px);
}
.my-active-class {
transition: all 1s ease;
}
.my-to-class {
transform: translateX(50px);
}
</style>
</head>
<body>
<div id="app">
<button @click="show=!show">{{show ? '隐藏动画' : '显示动画'}}</button>
<transition
appear-class="my-appear-class"
appear-active-class="my-active-class"
appear-to-class="my-to-class"
appear
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
>
<div class="box">
首次加载会从右边滑入
</div>
</transition>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
show: false
}
},
methods: {
beforeEnter(el) {
//设置状态,列如一个样式...
el.style.border="1px solid red"
},
enter(el,cb){
//自定义结束回调
el.addEventListener('transitionend',function (){
//做我们自己的业务...
el.style.border='1px solid blue'
el.innerText='任务完成!'
//执行Vue的默认回调,(可选)...
cb()
})
},
afterEnter(el) {
alert('动画完全结束了!!!')
}
}
})
</script>


