写在前面
- 说到优化是个很大话题,估计写本书也说不完。本文只针对前端动画的优化来浅述。
- 本文概念性的东西比较多(如:reflow、repaint),理解概念是前提。其中重排和重绘是两个重要概念(因为它们是导致卡顿的重要原因,其中重排的成本比重绘高很多)。
重排
- 概念:即reflow,当我们在某个元素上执行动画时,浏览器需要每一帧都检测是否有元素受到影响,并调整他们的大小,位置,通常这种调整都是联动的,我们称为reflow。
- 触发重排的相关属性:
- 盒子模型相关属性会触发重布局
- width
- height
- padding
- margin
- display
- border-width
- border
- min-height
- 定位属性及浮动也会触发重布局
- top
- bottom
- left
- right
- position
- float
- 改变节点内部文字结构也会触发重布局
- text-align
- overflow-y
- font-weight
- overflow
- font-family
- line-height
- vertival-align
- white-space
- font-size
- 盒子模型相关属性会触发重布局
重绘
- 概念:即repaint,动画执行时,浏览器还需要监听元素的外观变化,通常是背景色,阴影,边框等可视元素,并进行重绘,我们称为repaint。
- 触发重绘的相关属性:
- color
- border-style
- border-radius
- visibility
- text-decoration
- background
- background-image
- background-position
- background-repeat
- background-size
- outline-color
- outline
- outline-style
- outline-width
- box-shadow
浏览器是如何渲染DOM的
- 获取DOM后分割为多个图层
- 对每个图层的节点计算样式结果(Recalculate style--样式重计算)
- 为每个节点生成图形和位置(Layout--回流和重布局)
- 将每个节点绘制填充到图层位图中(Paint Setup和Paint--重绘)
- 图层作为纹理上传至GPU
- 符合多个图层到页面上生成最终屏幕图像(Composite Layers–图层重组)
创建新的渲染层
创建新的渲染层,执行动画时,只需要GPU按照现有的位图,按照相应的变换在独立的渲染层中输出,然后再合并输出。这个过程并不需要主线程CPU的参与,这就是优化的核心点。
创建新的渲染层的情况:
- 当一个元素位于HTML文档的最外层(元素)
- 当一个元素position不为initial,并且拥有一个z-index值(不为auto)
- 当一个元素被设置了opacity,transforms, filters, css-regions, paged media等属性。
- (当然还会有其他情况,具体可参考下面的参考链接)
JS动画和CSS3动画的比较
- js动画:优点是我们能随时控制开始,暂停,停止。缺点是没办法像css这样优化,因为js动画是在主线程上跑的,容易卡顿丢帧。
- css3动画:优点是浏览器可以对动画进行优化。它必要时可以创建图层,然后在主线程之外运行。缺点是缺乏强大的控制能力。
结论
- 我们应该尽力避免使用会触发重布局和重绘的属性,以免失帧。最好提前申明动画,这样能让浏览器提前对动画进行优化。
- 现在用来做动画的最好属性是如下几个:opacity、translate、rotate、scale,尽量避免left/padding/background-position等。
- 如果需要JS执行动画,使用requestAnimationFrame,或者Velocity,避免使用jQuery动画,setTimeout,setInterval。
- 尽可能的为产生动画的元素使用fixed或absolute的position。
- 使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰复合层的排序,可以有效减少chrome创建不必要的复合层,提升渲染性能,移动端优化效果尤为明显。
- 然而并不是所有浏览器默认都会开启GPU渲染,所以通常会用translate3d,translateZ,或者是opacity < 1等来强制开启。