4.绘制。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
5.合成。由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。
当然,不一定每帧都总是会经过管道每个部分的处理。我们的目标就是,每一帧的动画,对于上述的管道流程,能避免则避免,不能避免则最大限度优化。
优化动画步骤先给出一个步骤,调优一个动画,有一定的指导原则可以遵循,一步一步深入动画:
1.精简 DOM ,合理布局这个没什么好说的,如果可以,精简 DOM 结构在任何时候都是对页面有帮助的。
2.使用 transform 代替 left、top,减少使用耗性能样式现代浏览器在完成以下四种属性的动画时,消耗成本较低:
position(位置):transform: translate(npx, npx)
scale(比例缩放):transform: scale(n)
rotation(旋转) :transform: rotate(ndeg)
opacity(透明度):opacity: 0...1
如果可以,尽量只使用上述四种属性去控制动画。
不同样式在消耗性能方面是不同的,改变一些属性的开销比改变其他属性要多,因此更可能使动画卡顿。
例如,与改变元素的文本颜色相比,改变元素的box-shadow将需要开销大很多的绘图操作。 改变元素的width可能比改变其transform要多一些开销。如box-shadow属性,从渲染角度来讲十分耗性能,原因就是与其他样式相比,它们的绘制代码执行时间过长。
这就是说,如果一个耗性能严重的样式经常需要重绘,那么你就会遇到性能问题。其次你要知道,没有不变的事情,在今天性能很差的样式,可能明天就被优化,并且浏览器之间也存在差异。
开启 GPU 硬件加速
归根结底,上述四种属性的动画消耗较低的原因是会开启了 GPU 硬件加速。动画元素生成了自己的图形层(GraphicsLayer)。
通常而言,开启 GPU 加速的方法我们可以使用
will-change: transform
这会使声明了该样式属性的元素生成一个图形层,告诉浏览器接下来该元素将会进行 transform 变换,让浏览器提前做好准备。
使用will-change并不一定会有性能的提升,因为即使浏览器预料到会有这些更改,依然会为这些属性运行布局和绘制流程,所以提前告诉浏览器,也并不会有太多性能上的提升。这样做的好处是,创建新的图层代价很高,而等到需要时匆忙地创建,不如一开始直接创建好。
对于 Safari 及一些旧版本浏览器,它们不能识别will-change,则需要使用某种 translate 3D 进行 hack,通常会使用
transform: translateZ(0)
所以,正常而言,在生产环境下,我们可能需要使用如下代码,开启硬件加速:
{ will-change: transform; transform: translateZ(0); }
3.控制频繁动画的层级关系动画层级的控制的意思是尽量让需要进行 CSS 动画的元素的z-index保持在页面最上方,避免浏览器创建不必要的图形层(GraphicsLayer),能够很好的提升渲染性能。
OK,这里又提到了图形层(GraphicsLayer),这是一个浏览器渲染原理相关的知识(WebKit/blink内核下)。它能对动画进行加速,但同时也存在相应的加速坑!
简单来说,浏览器为了提升动画的性能,为了在动画的每一帧的过程中不必每次都重新绘制整个页面。在特定方式下可以触发生成一个合成层,合成层拥有单独的 GraphicsLayer。
需要进行动画的元素包含在这个合成层之下,这样动画的每一帧只需要去重新绘制这个 Graphics Layer 即可,从而达到提升动画性能的目的。
那么一个元素什么时候会触发创建一个 Graphics Layer 层?从目前来说,满足以下任意情况便会创建层:
硬件加速的 iframe 元素(比如 iframe 嵌入的页面中有合成层)
硬件加速的插件,比如 flash 等等
使用加速视频解码的<video>元素
3D 或者 硬件加速的 2D Canvas 元素
3D 或透视变换 (perspective、transform) 的 CSS 属性
对自己的 opacity 做 CSS 动画或使用一个动画变换的元素
拥有加速 CSS 过滤器的元素
元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
元素有一个 z-index 较低且包含一个复合层的兄弟元素
本小点中说到的动画层级的控制,原因就在于上面生成层的最后一条:
元素有一个 z-index 较低且包含一个复合层的兄弟元素。
这里是存在坑的地方,首先我们要明确两点:
1.我们希望我们的动画得到 GPU 硬件加速,所以我们会利用类似transform: translateZ()这样的方式生成一个 Graphics Layer 层。