【译】如丝般顺滑-使用css3实现60帧的动画
原文: Smooth as Butter: Achieving 60 FPS Animations with CSS3
在移动端上实现动画很简单。
如果采取我们的建议的话,在移动端正确的实现动画也会比较容易。
虽然现在很多人在手机上运用CSS3动画,但许多人用的都不够恰当。很多应加以考虑的最佳实践常常被忽略,因为仍然有人不明白这些最佳实践的真正意义。
如今有这么多的设备规范,如果还不有针对性地优化你的代码,糟糕的用户体验将让你死无葬身之地。
记住:虽然市场上始终有一些高端的旗舰机在挑战性能极限,但你面对的仍将是和这些性能怪兽相比只是玩具一样的低端设备。
我们想帮助你正确地驾驭 CSS3。首先先要了解几件事。
理解时间轴
当渲染和处理HTML元素时,浏览器做了什么?这个非常简单的时间轴我们称之为 关键渲染路径
想要达到流畅的动画效果,我们需要关注修改属性会对 composite (合成)阶段造成怎样的影响。而不是去关注前面的其他阶段。
1. Styles
浏览器开始计算样式以应用在元素上——重新计算样式
2. Layout
接下来,浏览器会开始为每个元素生成用于布局的形状和位置信息。在该步骤浏览器会设置的页面属性包括 width
和height
,还有margin
,以及left/top/right/bottom
等。
3. Paint
在该步骤,浏览器开始用像素渲染填充每个元素,此时用到的属性有 box-shadown
, border-radius
,color
, background-color
等。
4. Composite
这步就是你施展拳脚的地方了,浏览器开始在屏幕上渲染所有的元素。
现代浏览器可以使用transform
和opacity
属性很好的实现四种动画。
- 位置 —— transform: transformX(n) transformY(n) translateZ(n);
- 缩放 —— transform: scale(n);
- 旋转 —— rotate(ndeg);
- 透明 —— opacity:n;
如何达到每秒60帧
想法有了,可以撸起袖子干活了。
首先从HTML开始,创建一个简单的结构,把类名为app-menu
的元素放在一个类名为layout
的元素中。
<div class="layout">
<div class="app-menu"></div>
<div class="header">
<div class="menu-icon"></div>
</div>
</div>
用错误的方法来实现这个效果
1 | .app-menu { |
看见我们这些我们修改的属性了吗?我们应该避免使用left/top/right/bottom
这些属性作为动画。这些属性不能实现流畅的动画,因为他们会让浏览器每次都创建布局,而这会影响他们的子元素。
这样做的结果大概是这样的:
这个动画一点都不流畅。我们使用开发者工具的时间轴来看看发生了什么,结果如下:
这清楚地显示了FPS是不平整的并且表现也很慢。
绿色的条代表FPS,高的条表示动画渲染的帧数达到60FPS。低的条表示小于60帧。所以理想情况下,你想让绿色的长条贯穿整个时间轴。红色的条代表着糟糕的性能,所以另一方面评估程序的方法是消除这些红色的条。
使用 Transform
1 | .app-menu { |
transform属性影响Composite(合成)属性。这里我们告诉浏览器布局将被渲染并且已经准备好,当动画开始时。所以让渲染动画时卡顿更少。
时间轴的显示如下:
结果变的好点了,FPS更加规律了,因此动画更顺畅了。
使用GPU运行动画
让我们来上升一个等级,准备好让动画变得如丝般顺滑,我们开始使用GPU来渲染动画。
1 | .app-menu { |
虽然一些浏览器仍然需要 translateZ()
或 translate3d()
作为备选方案,但will-change
被广泛支持已经是势不可挡了。它的功能是把元素提升到另一个层中,这样浏览器就不必关心布局渲染或者绘制了。
看见有多顺畅了吗? 时间轴目前是这样的:
动画的FPS更稳定了,渲染也更快了。但是有一帧仍然渲染得很久。在开始处还有一点点瓶颈。
还记得开始时创建的HTML结构吗?让我们来通过JavaScript来控制类名为app-menu
的元素。
1 | function toggleClassMenu() { |
这儿的问题是给布局中的div元素增加了类名,这样使得浏览器多了一次重新计算样式的步骤,因此影响了渲染表现。
如丝般顺滑的60帧动画解决方案
如果我们在视窗外的区域创建menu
元素呢?在脱离主区域的地方这么做,可以确保影响仅限于你想赋予动画的元素。
因此,我们打算使用下面的HTML结构:
1 | <div class="menu"> |
现在我们可以以略微不同的方式来控制菜单的状态。我们可以通过使用JavaScript的transitionend
方法,在动画结束时移除类名来控制动画。
1 | function toggleClassMenu() { |
我们来整理一下代码然后检查下结果。
下面是完整的CSS3代码示例:
1 | .menu { |
那么时间轴表现如何呢?
是不是如丝般顺滑呢?
翻译总结:
目前在给手机页面通过CSS3实现简单动画时,一般很少考虑流畅问题。因为在开发阶段使用的PC机性能都极高,很难主要到这一点。如我目前使用的Mac Pro(i7,16g)。即便使用文中提到的错误示例(第一种方案,通过left控制动画),通过审查工具分析时,其帧数依旧如诗般顺滑...,然而应该当考虑到实际的使用场景是移动端,且就目前而言,确实仍有一大批人在只用较为低端的安卓机。所以采用最佳实践很有必要,并且实际使用的动画会比菜单滑动要复杂。所以是否采取最佳实践方案在实际的设备中运行会出现较大差异。
【译】如丝般顺滑-使用css3实现60帧的动画