FLIP 动画技术
如果用 height
, width
, top
, left
等CSS属性来做动画,性能要远远差于用 transform
或 opacity
属性的动画。动画元素多的话,前者会带来页面卡顿。
这是因为不同的动画,涉及浏览器的不同阶段:Layout ➠ Pain ➠ Composite。如果单独触发 Composite,那么浏览器的负担就很小,动画会很丝滑。
When any property that triggers layout changes (such as
height
), the browser has to recursively check if any other element’s layout has changed as a result, and that can be expensive.
触发布局的属性一旦发生变化,浏览器就会非常忙碌.它需要递归的计算其它元素的布局是否会发生变动.
一旦这个计算时长超过1个动画帧(一般是60帧每秒,也就是说超过16.7ms), 那么这帧动画将不会绘制,产生页面卡顿。
FLIP动画技术,就是只利用transform
或 opacity
模拟布局变动的技巧,因为它不触发Layout,所以动画比较丝滑。
FLIP是 First, Last, Invert, Play的简称。
First
对应动画的Start阶段,用 element.getBoundingClientRect()
记录初始位置。
Last
对应动画的End阶段,先执行触发layout变动的代码,记录元素的终止位置。
Invert
现在元素处于End位置,利用 transform
做一个逆运算,让添加了 transform
的元素回归到初始位置。
Play
真正需要执行动画时,将 transform
置为 None.
采用Web Animations API的代码示例如下
const elm = document.querySelector('.some-element');
// First: get the current bounds
const first = elm.getBoundingClientRect();
// execute the script that causes layout change
doSomething();
// Last: get the final bounds
const last = elm.getBoundingClientRect();
// Invert: determine the delta between the
// first and last bounds to invert the element
const deltaX = first.left - last.left;
const deltaY = first.top - last.top;
const deltaW = first.width / last.width;
const deltaH = first.height / last.height;
// Play: animate the final element from its first bounds
// to its last bounds (which is no transform)
elm.animate([{
transformOrigin: 'top left',
transform: `
translate(${deltaX}px, ${deltaY}px)
scale(${deltaW}, ${deltaH})
`
}, {
transformOrigin: 'top left',
transform: 'none'
}], {
duration: 300,
easing: 'ease-in-out',
fill: 'both'
});
注意
- 要设置
transform-origin: top left
- 可以采用第三方动画库来实现FLIP
提出FLIP动画技术的大牛 David Khourshid 还专门编写了Flipping.js,可以更加方便的实现高性能FLIP动画。