为什么css动画比JavaScript控制动画高效?

1,732 阅读5分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

为什么css动画比JavaScript控制动画高效?

在思考这个问题之前,我们先来了解几个概念:

  1. 重排
  2. 重绘
  3. 合成

1.必备知识

1.重排(layout)

定义:当通过js或者css修改元素的几何属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排.无疑,重排需要更新完成的渲染流水线(对渲染流水线不了解的同学可以百度,可以简单理解为页面元素重新渲染),所以开销也是最大的.

重排的触发条件:

  • 添加或者删除可见的DOM元素
  • 元素位置改变
  • 元素尺寸改变
  • 元素内容改变(例:一个文本被另一个不同尺寸的图片代替)
  • 页面渲染初始化(无法避免)
  • 浏览器窗口尺寸改变

2.重绘(repaint)

定义:当通过js或者css改变修改元素的绘制属性,例如改变元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段(就是生成绘制列表),然后执行之后的一系列子阶段,这个过程就叫重绘,相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些

触发条件:

  • background属性
  • outline属性
  • box-shadow属性
  • border属性
  • visibility

3.合并(Composite)

定义:

更改一个即不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成.比如我们使用了CSS的transform属性来实现动画效果,就可以避免重排和重绘,直接在非主线程上执行合成动画操作.这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率

触发条件:

  • will-change
  • transform属性改变
  • 整个图层的几何变化,透明度变换,阴影

2.为什么css比js高效的结论

看到这里,相信大家心里已经有了答案:不就是css的动画可以直接触发合并,避开重绘和重排嘛!所以css动画比js动画更加高效.结论可以说是对的,但也不完全对.因为css和js没有绝对的高效,只是在针对一些情况的时候,各有各的优势.

那我们既然知道了css在某些地方的高效,能不能和js做一个对比,好明白什么时候用css效果更好,什么时候用js效果更好呢?当然可以,毕竟我们码代码,就是在各种选择里面找到最优解.

3.css动画和js动画在不同的需求下应该如何选择

通过合成的定义我们知道,一些css动画是可以在非主线程里面完成的,在主线程中,维护了一颗Layer树,管理了TiledLayer,在非主线程,维护了同样一颗LayerTreeHostImpl,管理了LayerImpl,这两棵树的内容是拷贝关系。因此可以彼此不干扰,当Javascript在主线程操作LayerTreeHost的同时,非主线程可以用LayerTreeHostImpl做渲染。当Javascript繁忙导致主线程卡住时,合成到屏幕的过程也是流畅的。为了实现防假死,鼠标键盘消息会被首先分发到非主线程,然后再到主线程。这样,当主线程繁忙时,非主线程还是能够响应一部分消息,例如,鼠标滚动时,加入主线程繁忙,非主线程也会处理滚动消息,滚动已经被提交的页面部分(未被提交的部分将被刷白)。

那么我们可以得出结论,css动画比js动画流畅的前提是:

  • js执行一些昂贵的任务

  • 同时css动画不触发重排和重绘

    在css动画或js动画触发重排和重绘时,需要主线程进行Layer树的重计算,这时CSS动画或JS动画都会阻塞后续操作。

参考CSS Triggers,只有如下属性的修改才符合“仅触发合成(Composite),不触发重排(layout)或重绘(repaint)”:

  • backface-visibility
  • opacity
  • perspective
  • perspective-origin
  • transfrom

所以只有用上了3D加速或修改opacity时,才有机会用得上CSS动画的这一优势。

因此,在大部分应用场景下,效率角度更值得关注的还是下列问题。

  • 是否导致重排(layout)
  • 重绘(repaint)的面积
  • 是否是有高消耗的属性(css shadow等)
  • 是否启用硬件加速

4.css动画和js动画的不同点

以上,得出今天的精简对比部分.现今CSS动画和JS动画主要的不同点是

  • 功能涵盖面,js比css3要大

    • 定义动画过程的@keyframes不支持递归定义,如果有多种类似的动画过程,需要调节多个参数来生成的话,将会有很大的冗余(比如jQuery Mobile的动画方案),而JS则天然可以以一套函数实现多个不同的动画过程
    • 时间尺度上,@keyframes的动画粒度粗,而JS的动画粒度控制可以很细
    • CSS3动画里被支持的时间函数非常少,不够灵活
    • 以现有的接口,CSS3动画无法做到支持两个以上的状态转化
  • 实现/重构难度不一,CSS3比JS更简单,性能调优方向固定

  • 对于帧速表现不好的低版本浏览器,CSS3可以做到自然降级,而JS则需要撰写额外代码

  • CSS动画有天然事件支持(TransitionEndAnimationEnd,但是它们都需要针对浏览器加前缀),JS则需要自己写事件

  • CSS3有兼容性问题,而JS大多时候没有兼容性问题