前端必学的动画实现思路!
一个合理的动画是良好用户体验中必不可少的一部分。我们平常是怎样写动画的?CSS 中的 animation 和 transition,还有 requestAnimationFrame?
示例
请看下面的示例:
这是一个可添加的数字的随机乱序列表。首先想一想,我们第一直觉可能会这样做:将这些数字的 DOM 节点用绝对定位来布局,数字变化后计算 top、left 的值,再配合 transition 实现该动画。这种方式看似简单,其实内部要维护各种位置信息,所有坐标都需要手动管理,相当繁杂,非常不利于后期扩展。如果这些节点换成高度不固定的图片,那计算量可想而知。
那有没有一种更好的方式实现呢?肯定的,接下来介绍一个金光闪闪的概念:FLIP。
提前预览:
https://minjieliu.github.io/react-flip-demo
FLIP
FLIP 其实是几个单词的缩写:即 First、Last 、Invert 、Play。
让我们分解一下:
First
涉及动画的元素的初始状态(比如位置、缩放、透明等)。
Last
涉及动画的元素的最终状态。
Invert
这一步为核心,即找出这个元素是如何变化的。例如该元素在 First 和 Last 之间向右移动了 50px,你就需要在 X 方向 translateX(-50px),使元素看起来在 First 位置。
这里有一个知识点值得注意,DOM 元素属性的改变(比如 left、right、transform 等),会被集中起来延迟到浏览器下一帧统一渲染,所以我们可以得到一个这样的中间时间点:DOM 位置信息改变了,而浏览器还没渲染[1]。也就意味着在一定的时间内,我们能获取 DOM 改变后的位置,但在浏览器中位置还未改变。经测试,这个过程超过 10ms 就显得不稳定了。因此 setTimeout(fn, 0)、 React useEffect 和 Vue $nextTick 都可以实现 Invert 过程。
Play
即从 Invert 回到最终状态,有了两个点的位置信息,中间的过渡动画就可以使用 transition 实现。本文采用 Web Animation API[2] 实现,动画执行过程中不会添加 CSS 到 DOM 上,相当干净。
实现
这里主要使用 React 方式实现该效果,其他框架原理都一样可参考。
一个列表,将子元素 5 列为一行:
.list {
display: flex;
flex-wrap: wrap;
width: 400px;
}
.item {
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
border: 1px solid #eee;
}function ListShuffler() {
const [data, setData] = useState([0, 1, 2, 3, 4, 5]);
const listRef = useRef(null);
return (
{data.map((item) =