vue + translate动画之坑

先声明下,这里的坑并不是vue或浏览器的bug,只是我们在做translate动画时容易忽略的一个点.

假设我们要实现这样的效果:

一般我们会先实现无动画版,请看:


嗯,看起来挺好的~
接下来加上动画:
在模板中添加transition="slide",在stylus中添加相应的动画属性设置:
1
2
3
4
5
.slide
&-transition
transition: transform .4s
&-enter, &-leave
transform: translateX(100%)


实际效果:

咦?为什么怪怪的?蓝色进入的时候,还勉强过得去,红色进入就显得那么突兀呢?而且是等蓝色完全移出之后,红色突然出现的,神奇。
通过延长动画持续时间,调试发现,在动画结束之前,红色块和蓝色块会同时出现在文档中(display: block),而他们没有使用定位,都是处在正常文档流中,再加上外层的容器(div.wrapper)设置了overflow: hidden,所以无论如何切换,在动画结束之前,始终是蓝色在上,红色在下。如图:

在红色进入的那次切换,等到蓝色块动画结束后,DOM被隐藏(display:none),红色块才可以进入视野。

那如何解决这个问题呢?
答案就是使用绝对定位,使红色块和蓝色块的初始状态定位在同一位置,再通过translate做动画,两者动画互不影响。接下来是这个样子:

还有一个小的问题,使用绝对定位后,定位层的高低值,由DOM的顺序决定,也就是说红色块始终盖过蓝色块,我们需要在切换的时候给进入的快增大z-index值,所以最终版本就是这样:


再补充一下我理解的vue中实现transition指令动画的原理。
v-ifslidetransition名为例,先讲一下使用方法:
给使用了v-if的元素,添加transition="slide",然后在stylus中添加:

1
2
3
4
5
.slide
&-transition
transition: transform .4s // 设置过渡属性和持续时间
&-enter, &-leave // 设置进入开始瞬间和离开结束瞬间的样式
transform: translateX(100%)

然后就可以愉快的开始动画了~看起来还是比较简单的。
实际上,vue是这么干的:
始终为该元素添加slide-transitionclass,使transition的动画基础设置生效。

在该元素进入时,添加slide-enterclass,此时样式的状态是transform: translateX(100%),随后立即将slide-enter这个class删掉,目标样式是默认的transform: translateX(0), 然后动画开始生效,在0.4s后,动画结束。

在该元素离开时,此时的样式是默认的transform: translateX(0), 添加slide-leaveclass,目标样式是transform: translateX(100%), 动画开始生效,在0.4s后,触发事先注册的transitionend事件,在这个回调里把这个DOM移除。

注意,如果使用了transition指令,但由于某些原因动画没有发生,那么本该移除的DOM将不会被移除,因为移除操作是在transitionend事件中被调用的,因为动画没发生,那么transitionend也不会被调用,DOM也不会被移除了。