FLIP你的动画
次访问
网页中的动效应该运行在60fps,达到这个帧率并不容易,它去取决于你做了多大的尝试,这里我将用FLIP来帮助你。
我已经写了一个FLIP库,在这里你可以看到文档和demos。
FLIP本质上是一个原则,而不是框架或库。这是一种思考动画的方式,尝试让浏览器渲染动画的开销尽可能小,如果一切顺利,应该能达到60fps的动画。
基本概念
一般动画直接开始后,在每帧可能做开销大的计算,而这里我们让动画从头开始,动态的重新计算动效属性,然后让浏览器简单的执行渲染。
FLIP代表First,Last,Invert.Play。
- First: 元素涉及到动效的初值状态
- Last: 元素的最终状态
- Invert: 指出从动画开始到结束元素要如何变化,比如
width
、height
、opacity
。然后用transform
、opacity
来反向设置。如果元素从开始到结束下移了90px,你就要应用transformY(-90px)
,让元素看起来和开始时一样,虽然实际上不是。 - Play: 开始变化任何你改变的属性,然后去掉逆变换。因为元素处于最终位置,所以反向设置中的
transform
、opacity
,将使它们从模拟的第一个位置轻松变换到最后一个位置。
代码分解
1 | // 获取初始位置 |
然而,也可以用即将来临的Web Animations API,这个会更简单,只是需要Web Animations API polyfill,不过这个补丁很轻量,并且确实很实用。
1 | // 获取初始位置 |
FLIP的用途
当你在响应到用户输入后用一些动画来响应,这绝对是非常棒的。比如,在Chrome开发峰会上,我展开用户点击的卡片,通常情况下,元素开始和结束的位置、大小都不知道,因为网页是响应式的,元素位置大小都不固定,但FLIP就很有用,它能明确的计算元素,在运行时给出正确的值。
你能够做这种相对昂贵的预计算是因为利用了用户感知。用户和你的网页有交互之后,你有趁他们不注意的100ms时间来做这些。在这100ms内,用户会觉得网站是立刻响应了的,而你只需要在动画过程中保持60fps。
我们可以利用这个感知期(100ms),通过javascript做getBoundingClientRect
操作(或者你非要用不优雅的getComputedStyle
),这样我们使动画细腻流畅,利于重绘。
可以用transform
和opacity
重写动画是最好的,如果你在js或CSS中没有用这些属性,那你可以开始优化了,它们会在你改变布局属性(比如width
、height
、left
、top
)时,用开销小的属性重写后达到最好的优化效果。
有时你为了用FLIP需要重新构思你的动画,在多数情况下,我把动画元素单独提取出来,这样我就可以不失真的制作动画了,并且尽可能多的用FLIP。你可能会觉得这样应用过度了,但我觉得不是,因为:
1、大家想这样。我的一个同事兼好友最近做了一个关于人们想从新闻app上得到什么的调查,最多的回答(这让他很吃惊)不是离线支持、同步、通知,或类似的东西,而是浏览平滑——没有晃动、没有卡顿、没有颤抖。
2、程序员就是这么做的。当然,这是一种主观的衡量标准,但我已经听过很多次,程序员花了好几天的时间才把过渡做得恰到好处。通过运维服务,我们我网站加载速度都很快,用户会根据他们的操作体验来评价我们的网站,而这些细节将使我们与众不同。
注意事项
使用FLIP时要注意以下几点:
1、不要超过100ms的窗口期。
记住一定不要超过100ms,如果超过了,你的应用会表现得没反应。通过DevTools关注它,了解是否超出了100ms。
2、认真设计动画。
想象下,你在运行一个动画,做一些位移和透明度得改变,然后你决定运行另外一个动画,它需要大量得预计算,这会打断之前运行中的动画,这种情况很糟糕。这里的关键是保证你的预计算工作是在空闲时间或上面说过的100ms窗口期内完成的,这两个动画不会互相阻断。
3、内容会失真。
当你使用scale
和transform
,一些元素会失真。上面说过,我会调整一点结构,来不失真的使用FLIP,不过这可能有争议。
总结
我喜欢用FLIP来考虑动画的实现,因为它让JS和CSS配合的很好,用JS来计算,但用CSS处理动画过程,你不需要用CSS来实现动画,虽然你可以只用简单的Web Animations API或JS,或什么其他的简单的方式。主要的一点是,你正在降低每帧的复杂性和成本(通常意味着transform
和opacity
),以尽量为用户提供最好的体验。
所以,使用FLIP吧。