前端浏览器动画性能优化

先总结几个要点

  1. 精简DOM,合理布局

  1. 使用transform代替left、top减少使用引起页面重排的属性

  1. 开启硬件加速

  1. 尽量避免浏览器创建不必要的图形层

  1. 尽量减少js动画,如需要,使用对性能友好的requestAnimationFrame

  1. 使用chrome performance工具调试动画性能

我们知道网页动画的每一帧都是一次重新渲染,每秒低于24镇的动画,人眼就能感受到停顿,每秒30-60帧才能比较流畅 浏览器会按照大多数显示器的刷新频率60Hz来刷新动画, 如果想达到60FPS,就意味着每一帧的任务耗时不能高于16毫秒。

通过下图我们可以了解浏览器渲染每一帧的过程

前端浏览器动画性能优化 - 图1

img

1.JavaScript。一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如:CSS Animations、Transitions 和 Web Animation API。

2.样式计算。此过程是根据匹配选择器(例如 .headline 或 .nav > .nav__item)计算出哪些元素应用哪些 CSS 3. 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。

3.布局。在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。

4.绘制。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。

5.合成。由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。

“生成布局”(flow)和”绘制”(paint)这两步,合称为”渲染”(render)。重新渲染就是需要重新生成布局和重新绘制。 有上述的渲染流水线我们可以得知重绘不一定需要重排,重排必然导致重绘

重排和重绘会不断触发,这是不可避免的。但是,它们非常耗费资源,是导致网页性能低下的根本原因。 提高网页性能,就是要降低”重排”和”重绘”的频率和成本,尽量少触发重新渲染。

重排还重绘会消耗大量的CPU和GPU资源,前端新能优化最主要的优化点就是尽量减少重绘和重排。

前端浏览器动画性能优化 - 图2

image

影响网页渲染的因素

其中最简单的,样式表越简单,重绘和重排越快,重绘和重排的DOM元素层级越高,成本就越高,所以我们在开发前端页面时就需要精简DOM元素,合理布局。

另外Table元素的重排和重绘成本要高于div,所以我们提倡使用div+css布局,尽量避免使用table布局。

还有其他对渲染性能有影响的操作,比如:

  • DOM元素读写分离
  • 让进行大量动画的元素脱离文档流,减少重排开销
  • 通过改变元素的class或csstext一次性的更改样式
  • 缓存DOM元素的位置信息,避免不必要的属性读取
  • 尽量使用离线DOM
  • 使用css3 transform优化动画性能

使用css3 transform

该CSS属性可以旋转,缩放,倾斜,或者上传给定的元素。这是通过修改CSS 可视格式模型的坐标空间来实现的。
如果该属性的值不是none,则会创建一个堆叠上下文。在这种情况下,该对象将充当position: fixed的包含块(所以position: fixed的元素将会被他覆盖)。

css3 transform 的执行效率

我们通过一个例子来解释为什么transform的动画执行效果更佳。

  1. <!-- 对应图1-->
  2. div { height: 100px; transition: height 1s linear; }
  3. div:hover { height: 200px; }
  4. <!-- 对应图2 -->
  5. div { transform: scale(0.5); transition: transform 1s linear; }
  6. div:hover { transform: scale(1.0); }

一个从 height: 100px 到 height: 200px 的 动画按照下面的流程图来执行各种操作 橙色方框的操作比较耗时,绿色方框的操作比较快速

前端浏览器动画性能优化 - 图3

1

前端浏览器动画性能优化 - 图4

2

因为每一帧的变化浏览器都在进行布局、绘制、把新的位图交给 GPU 内存,但是在将位图加载到GPU内存中的操作是个相对耗时的操作。

GPU 在如下方面很快:

  • 绘制位图到屏幕上
  • 可不断的绘制相同的位图
  • 将同一位图进行位移、旋转、缩放

我们看使用了transform属性的动画执行过程(图二),这个无疑是效率最优的执行方式。

层的引入(参考-无线性能优化:Composite

页面一旦在装入并解析完成后,就会表示为许多Web开发者所熟悉的结构:DOM。然而,在页面的渲染过程中,浏览器还具有一系列并不直接暴露给开发者的页面中间表示方式。这些表示方式中最重要的结构就是层。

在Chrome中实际上有几种不同类型的层:掌管DOM子树的渲染层(RenderLayer)以及掌管渲染层子树的图形层(GraphicsLayer),某些特殊的渲染层会被认为是合成层(Compositing Layers,合成层拥有单独的 GraphicsLayer。

拥有单独GraphicsLayer的层,都会将位图存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上。

什么渲染层会提升为合成层?Chrome在这方面采用的规则仍在随着时间推移逐渐发展变化,但在目前下面这些因素都会引起Chrome创建层:

  • 进行3D或者透视变换的CSS属性
  • 使用硬件加速视频解码的
  • 具有3D(WebGL)上下文或者硬件加速的2D上下文的元素
  • 组合型插件(即Flash)
  • 具有有CSS透明度动画或者使用动画式Webkit变换的元素
  • 具有硬件加速的CSS滤镜的元素
  • 子元素中存在具有组合层的元素的元素(换句话说,就是存在具有自己的层的子元素的元素)
  • 同级元素中有Z索引比其小的元素,而且该Z索引比较小的元素具有组合层(换句话说就是在组合层之上进行渲染的元素)

提升为合成层简单说来有以下几点好处

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

合成层的好处是不会影响到其他元素的绘制,因此,为了减少动画元素对其他元素的影响,从而减少 paint,我们需要把动画效果中的元素提升为合成层。

提升合成层的最好方式是使用 CSS 的 will-change 属性。从上一节合成层产生原因中,可以知道 will-change 设置为 opacity、transform、top、left、bottom、right 可以将元素提升为合成层。

对于那些目前还不支持 will-change 属性的浏览器,目前常用的是使用一个 3D transform 属性来强制提升为合成层:transofrm: translateZ(0)

通常情况下开启硬件加速会提高动画的流畅性,但是过多的合成层也会造成性能瓶颈,过多的合成层会占用大量的内存空间,

对于合成层占用内存的问题,这里有两个 demo 进行了验证。

demo 1demo 2 中,会创建 2000 个同样的 div 元素,不同的是 demo 2 中的元素通过 will-change 都提升为了合成层,而两个 demo 页面的内存消耗却有很明显的差别。

前端浏览器动画性能优化 - 图5

image

网页动画的渲染

有一些JavaScript方法可以调节重新渲染,大幅提高网页性能。

其中最重要的,就是 window.requestAnimationFrame() 方法。它可以将某些代码放到下一次重新渲染时执行。

  1. window.requestAnimationFrame(fn);

`\window.requestIdleCallback()*`*也可以用来调节重新渲染。它指定只有当一帧的末尾有空闲时间,才会执行回调函数。只有当前帧的运行时间小于16.66ms时,函数fn才会执行。否则,就推迟到下一帧,如果下一帧也没有空闲时间,就推迟到下下一帧,以此类推

  1. requestIdleCallback(fn);

它还可以接受第二个参数,表示指定的毫秒数。如果在指定 的这段时间之内,每一帧都没有空闲时间,那么函数fn将会强制执行。

  1. requestIdleCallback(fn, 5000);

Chrome Devtool Performance

怎么去分析页面运行时的性能表现,Chrome Devtool Performance是一个很好的选择。这里请大家参考这篇文章全新Chrome Devtool Performance使用指南