总结的要点
- 精简 DOM,合理布局
- 使用 transform 代替 left,top 减少使用引起页面重排的属性
- 开启硬件加速
- 尽量避免浏览器创建不必要的图形层
- 尽量减少 js 动画,如需要,使用对性能更友好的
requestAnimationFrame
- 使用 chrome performance 工具调试动画性能
我们知道网页动画的每一帧都是一次重新渲染,每秒低于 24 帧的动画,人眼会感受到停顿,30~60 帧/s 才比较流畅,大多数屏幕的刷新频率 60Hz,这就意味着每一帧的任务耗时不能高于 16ms
浏览器渲染每一帧的过程,如图:
- js 我们可以使用 js 来实现一些视觉变化的效果,也可以使用一些其他的常用方法:CSS Animations、Transitions 和 Web Animation API
- 样式计算 此过程根据匹配选择器计算出哪些元素应用哪些 CSS3 规则,并将应用规则并计算每个元素的最终样式
- 布局 浏览器开始计算元素占据的空间大小及在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,因此对于浏览器来说,布局过程是经常发生的
- 绘制 填充像素的过程。涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(层)上完成的
- 合成 由于页面的各部分可能被绘制到多层,由此他们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误出现在另一个元素的上层
生成布局(flow)和绘制(paint)合称渲染(render)。重排必然导致重绘,重绘不一定需要重排。
优化页面渲染:
- 精简 DOM 元素,合理布局,样式表尽可能简单,重绘和重排就越快
- 多使用 div+css 布局,避免使用 table 布局(table 元素的重排和重绘成本要高于 div)
- DOM 元素读写分离
- 让进行大量动画的元素脱离文档流,减少重排开销
- 通过改变元素的 class 或 csstext 一次性的更改样式
- 缓存 DOM 元素的位置信息,避免不必要的属性读取
- 尽量使用离线 DOM
- 使用 css3 transform 优化动画性能
css3 transform 的执行效率
<!-- 对应图1-- > div {
height: 100px;
transition: height 1s linear;
}
div:hover {
height: 200px;
}
<!-- 对应图2 -- > div {
transform: scale(0.5);
transition: transform 1s linear;
}
div:hover {
transform: scale(1);
}
一个从 height: 100px 到 height: 200px 的 动画按照下面的流程图来执行各种操作橙色方框的操作比较耗时,绿色方框的操作比较快速
因为每一帧的变化浏览器都在进行布局、绘制、把新的位图交给 GPU 内存,但是在将位图加载到 GPU 内存中的操作是个相对耗时的操作。
GPU 在如下方面很快:
- 绘制位图到屏幕上
- 可不断的绘制相同的位图
- 将同一位图进行位移、旋转、缩放
层的引入
在 Chrome 中实际上有几种不同类型的层:
- 掌管 DOM 子树的渲染层(RenderLayer)
- 掌管渲染层子树的图形层(GraphicsLayer)
- 某些特殊的渲染层被认为是合成层(Compositing Layers),合成层拥有单独的 GraphicsLayer
拥有单独 GraphicsLayer 的层,都会将位图存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上
在哪些因素下会将渲染层提升为合成层:
- 进行 3D 或透视变换的 css 属性
- 使用硬件加速视频解码的
<video>
元素 - 具有 3D(WebGL)上下文或硬件加速的 2D 上下文的
<canvas>
元素 - 组合型插件(即 Flash)
- 具有有 CSS 透明度动画或使用动画式 Webkit 变换的元素
- 具有硬件加速的 css 滤镜的元素
- 子元素中存在具有组合层元素的元素(存在具有自己层的子元素的元素)
- 同级元素中有 Z 索引币其小的元素,且该 Z 索引比较小的元素具有组合层(在组合层之上进行渲染的元素)
提升为合成层有什么好处
- 合成层的位图,会交由 GPU 合成,币 CPU 处理要快
- 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
- 对于 transform 和 opacity 效果,不会触发 layout 和 paint
提升合成层的最好方式是使用 CSS 的 will-change 属性,如 will-change 设置为 opacity/transform/top/left/bottom/right
对于还不支持 will-change 属性的浏览器,可以使用一个 3D transform 属性来强制提升为合成层:transform:translateZ(0)
通常情况下开启硬件加速会提高动画的流畅性,但是过多的合成层也会造成性能瓶颈,会占用大量的内存空间
网页动画的渲染
在 js 中有一些方法可以调节重新渲染,大幅提高网页性能
window.requestAnimationFrame(fn)
可以将某些代码放到下一次重新渲染执行window.requestIdleCallback(fn)
可以调节重新渲染。它指定只有当一帧的末尾有空闲时间,才会执行回调函数。只有当前帧运行时间小于 16.66ms 时,函数 fn 才会执行,否则,就推迟奥下一帧,下一帧没时间,继续推迟下去window.requestIdleCallback(fn, 5000)
如果在指定的这段时间内,每一帧都没有空闲时间,那么函数 fn 将会强制执行
Chrome Devtool Performance
怎么去分析页面运行时的性能表现,Chrome Devtool Performance是一个很好的选择。