某次被问到如何实现以下动画效果:
若干个元素卡片从上而下排列,当增加或删除某个卡片的时候,其余的卡片会以一种 transition
动画的形式移动到适当的位置上,而不是生硬地闪现
当时我恰好看过 Vue
中的内置组件 transition
的实现,意识到完全可以用 transition
组件的部分原理来完成这个效果,但是由于没有深入地探究过为什么是这样,只停留在表面,知其然而不知其所以然,所以尽管我知道如何实现这个效果,但很难解释为什么是这样,语言组织地比较困难
后来我无意间看到一篇文章 FLIP技术给Web布局带来的变化,立马恍然大悟,原来这个东西叫 FLIP
FLIP
FLIP是 First
、Last
、Invert
和 Play
四个单词首字母的缩写
First
,指的是在任何事情发生之前(过渡之前),记录当前元素的位置和尺寸,即动画开始之前那一刻元素的位置和尺寸信息,可以使用 getBoundingClientRect()
这个 API
来处理(大部分情况下其实 offsetLeft
和 offsetTop
也是可以的)
Last
:执行一段代码,让元素发生相应的变化,并记录元素在动画最后状态的位置和尺寸,即动画结束之后那一刻元素的位置和尺寸信息
Invert
:计算元素第一个位置(First
)和最后一个位置(Last
)之间的位置变化(如果需要,还可以计算两个状态之间的尺寸大小的变化),然后使用这些数字做一定的计算,让元素进行移动(通过 transform
来改变元素的位置和尺寸),从而创建它位于第一个位置(初始位置)的一个错觉
即,一上来直接让元素处于动画的结束状态,然后使用 transform
属性将元素反转回动画的开始状态(这个状态的信息在 First
步骤就拿到了)
Play
:将元素反转(假装在first
位置),我们可以把 transform
设置为 none
,因为失去了 transform
的约束,所以元素肯定会往本该在的位置(即动画结束时的那个状态)进行移动,也就是last
的位置,如果给元素加上 transition
的属性,那么这个过程自然也就是以一种动画的形式发生了
按照我的理解,就是对动画元素起止状态的一个量化,量化成一个公式,绝大部分的连续动画都可以通过套用这个公式来完成,提升动画的开发效率,更加详细的请自行参见 FLIP技术给Web布局带来的变化
实现卡片 Card增删动画
了解了 FLIP
这个概念之后,再来实现开头提到的那个动画效果,其实就很简单了
First
记录在动画开始之前每个卡片的位置和尺寸信息,这里因为卡片的尺寸在动画过程中其实是不会发生任何变化的, 所以可以略过这一步,只记录卡片的位置信息
另外,如果所有卡片的尺寸都是相同的,那么也无需记录所有卡片的位置信息,因为无论是插入卡片还是删除卡片,都只有那些位于位置坐标在变化卡片坐标的后面的卡片才会受到影响的,前面的是不会变的
// First
activeList.forEach((itemEle, index) =><