Web动画性能指南

引言

随着网页功能变得愈发复杂和精细,以及手机端H5发展中所遇到的硬件性能瓶颈,网页的运行时性能问题变得越来越突出。而用户对于网页运行时性能最直观的感受,莫过于UI操作的流畅程度。流畅或卡顿,爽或不爽,皆在于每个UI动画细节之间。本文旨在帮助理解动画卡顿与流畅的原因,卡顿问题的调试方法,以及从实践中总结出实现流畅动画的规律。为构建操作流畅的网页提供参考。

量化动画的流畅程度

动画的实现原理,是利用了人眼的“视觉暂留”现象,在短时间内连续播放数幅静止的画面,使肉眼因视觉残象产生错觉,而误以为画面在“动”。

动画相关的几个概念

  • :在动画过程中,每一幅静止画面即为一“帧”。
  • 帧率:即每秒钟播放的静止画面的数量,单位是fps(Frame per second)。
  • 帧时长:即每一幅静止画面的停留时间,单位一般是ms(毫秒)。
  • 跳帧(掉帧/丢帧):在帧率固定的动画中,某一帧的时长远高于平均帧时长,导致其后续数帧被挤压而丢失的现象。

身边的帧率(频率):

  • 10FPS达成基本视觉暂留
  • 25~30FPS 传统广播电视信号
  • 60FPS浏览器渲染刷新频率
  • 60~85HZ显示器刷新频率
  • 100HZ日光灯管闪烁频率

帧率能反映动画的流畅程度

  • 在网页中,帧率能够达到50~60fps的动画将会相当流畅,让人倍感舒适。
  • 帧率在30~50fps之间的动画,因各人敏感程度不同,舒适度因人而异。
  • 帧率在30fps以下的动画,让人感觉到明显的卡顿和不适感。
  • 帧率波动很大的动画,亦会使人感觉到卡顿。

用帧率量化动画流畅度的几个例子

帧率稳定在60fps左右的流畅动画

帧率稳定但低于30fps的卡顿动画(帧率低导致卡顿)

平均帧率高,但存在跳帧现象的卡顿动画(帧率不稳定导致卡顿)

几款帧率监测工具

Stats.js,侦听全局或指定位置的帧率,JS实现,所有浏览器可用

StatsJS

Chrome自带的帧率监测工具,用于侦听全局帧率,以及页面重绘耗时

Chrome

Chrome

Chrome

Chrome Timeline,杀手级监测 & 调试工具

Chrome

小结

帧率能够量化动画的流畅程度,流畅的动画一般具备两个特点:

  • 帧率高(接近60fps最佳)
  • 帧率稳定,波动少(极少出现跳帧现象)

了解浏览器的渲染机制

每当性能提升工作遇到瓶颈时,我们总是会尝试从底层实现机制中寻找突破口。动画性能优化也不例外,想要将动画性能提升到极致,首先我们需要知道浏览器是如何渲染每一帧的。

浏览器的渲染引擎

各厂出品的浏览器所用的渲染引擎不尽相同: IE使用Trident ,FireFox使用Gecko ,Safari使用WebKit ,Chrome 28+ 和 Opera 15+使用的是Blink(WebKit的分支)。

来看两个例子:

Webkit的渲染流程

Webkit的渲染流程

Gecko的渲染流程

Gecko的渲染流程

虽然浏览器款式如此之多,但当我们对比WebKit和Gecko这两款引擎的工作流程,会发现二者大体的流程以及一些关键环节还是基本相同的。

几个关键的工作环节:

  • HTML解析(Parse HTML)
  • 解析CSS(Parse CSS)
  • 生成渲染树(Render Tree / Frame Tree)
  • 排版/重排(Layout/Reflow)
  • 绘图/重绘(Painting)

各款渲染引擎的基本工作流程可以抽象为下图:

渲染引擎的基本工作流程

可见,对于通过修改HTML元素CSS样式实现的动画,每修改一次CSS,浏览器就会做一次上图中CSS解析及其随后的操作。 (但并不是每一次对CSS的修改都会重新排版和绘图)

通过工具监测浏览器渲染行为

目前对浏览器渲染行为的监测,实现较好的是Chrome与Safari,并且这两者能远程调试Android和iOS设备中的Chrome和Safari浏览器(Chrome在Android 4.0后开始支持)。

借助Chrome和Safari的Timeline工具,分析耗时较长的帧,我们便能定位到渲染耗时长的原因,并针对问题原因寻找解决方案。

Chrome Timeline

Chrome Timeline

Safari Timeline

Safari Timeline

现代浏览器的硬件加速渲染通道以及层模型(Layer model)

近些年来,现代浏览器借助于显卡的优势改变了渲染操作:通常被笼统的称为“硬件加速(hardware acceleration)”。

以Chrome为例,在硬件加速渲染通道下,复杂的页面会被分为多个层(Layer), Chrome对各个层分别进行排版、绘图,再将绘图结果作为“纹理”上传至GPU, 由GPU完成层的3D变换(transform)等操作,最后再将GPU渲染好的层图像进行复合操作(Compesite Layers),得到最终的画面结果。 因此,通过3D变换实现的位移、旋转、缩放将不会触发浏览器重绘(除非层的内容发生改变)。

可见,可以避免重绘的层模型对于动画调优有着重大意义。

通过工具“看到”层

Chrome

Chrome

Safari

Safari

通过分层减小重绘面积

Safari

如何创建新的层

从目前来看(Chrome、Safari等现代浏览器仍在不断迭代演进),以下情况下元素会创建自己的层(包括但不限于以下情况,待完善):

触发普通元素的分层:transform:translate3d属性(如transform:translateZ(0)

自带单独分层的元素

  • 使用加速视频解码的<video>元素
  • <iframe>元素
  • Flash等插件

动画调优的策略与技巧

提升每一帧性能(缩短帧时长,提高帧率)

  • 避免频繁的重排。
  • 避免大面积的重绘。
  • 优化JS运行性能。

保证帧率平稳(避免跳帧)

  • 不在连续的动画过程中做高耗时的操作(如大面积重绘、重排、复杂JS执行),避免发生跳帧。
  • 若高耗时操作无法避免,则尝试化解,比如:将高耗时操作放在动画开始或结尾处。将高耗时操作分摊至动画的每一帧中处理。

针对硬件加速渲染通道的优化

  • 通过层的变化效果(如transform)实现位移、缩放等动画,可避免重绘。
  • 合理划分层,动静分离,可避免大面积重绘。
  • 使用分层优化动画时,需要留意内存消耗情况(通过Safari调试工具)。

低性能设备优先调试

Android设备优先调试:移动设备的硬件配置一般低于桌面设备,而移动端设备中,Android设备相比于iOS设备性能普遍较差,因此在Andorid设备下性能问题更加明显,幸运的是Android可以借助Chrome自带的远程调试工具方便调试动画性能(Android 4.0+),所以优先调试Android设备可以更早地发现问题,并能更方便地解决问题。

扩展阅读

原文链接:http://alexorz.github.io/animation-performance-guide

发表评论

登录后才能评论