本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
为什么css动画比JavaScript控制动画高效?
在思考这个问题之前,我们先来了解几个概念:
- 重排
- 重绘
- 合成
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动画有天然事件支持(
TransitionEnd、AnimationEnd,但是它们都需要针对浏览器加前缀),JS则需要自己写事件 -
CSS3有兼容性问题,而JS大多时候没有兼容性问题