Skip to content

分层和合成机制:利用合成线程优化页面性能

注意一点,能直接在合成线程中完成的任务都不会改变图层的内容,如文字信息的改变,布局的改变,颜色的改变,统统不会涉及,涉及到这些内容的变化就要牵涉到重排或者重绘了。 能直接在合成线程中实现的是整个图层的几何变换,透明度变换,阴影等,这些变换都不会影响到图层的内容。

比如滚动页面的时候,整个页面内容没有变化,这时候做的其实是对图层做上下移动,这种操作直接在合成线程里面就可以完成了。

再比如旋转操作,如果样式里面使用了 will-change ,那么这些 box 元素都会生成单独的一层,那么在旋转操作时,只要在合成线程将这些 box 图层整体旋转到设置的角度,再拿旋转后的 box 图层和背景图层合成一张新图片,这个图片就是最终输出的一帧,整个过程都是在合成线程中实现的。

当你通过滚动条滚动页面,或者通过手势缩放页面时,屏幕上就会产生动画的效果。之所以你能感觉到有动画的效果,是因为在滚动或者缩放操作时,渲染引擎会通过渲染流水线生成新的图片,并发送到显卡的后缓冲区。大多数设备屏幕的更新频率是 60 次 / 秒,这也就意味着正常情况下要实现流畅的动画效果,渲染引擎需要每秒更新 60 张图片到显卡的后缓冲区。我们把渲染流水线生成的每一副图片称为一帧,把渲染流水线每秒更新了多少帧称为帧率,比如滚动过程中 1 秒更新了 60 帧,那么帧率就是 60Hz(或者 60FPS)。

为什么有些时候主线程卡住了,css 动画依然没有暂停?

因为有些 css 动画没有触发重排和重绘,只是运用了渲染进程中的合成合成线程进行处理,所以操作是在合成线程上完成的,这也就意味着在执行合成操作时,是不会影响到主线程执行的。这就是为什么经常主线程卡住了,但是 CSS 动画依然能执行的原因。

为什么打开网站有时候网页内容是模糊的,过一阵就变清晰了?

这是因为涉及到一个关键因素纹理上传,因为图片从计算机内存到 gpu 进程的缓冲区这个过程比较慢,所以浏览器采用了一个策略,就是先合成一个低分辨率的图片进行上传显示,然后继续绘制正常比例的图片,再替换原先展示的低分辨率图片。一开始显示低分辨率的图片就比用户什么也看不到的好。

如何利用分层技术优化代码?

在写 Web 应用的时候,你可能经常需要对某个元素做几何形状变换、透明度变换或者一些缩放操作,如果使用 JavaScript 来写这些效果,会牵涉到整个渲染流水线,所以 JavaScript 的绘制效率会非常低下。 这时你可以使用 will-change 来告诉渲染引擎你会对该元素做一些特效变换,CSS 代码如下:

css
.box {
  will-change: transform, opacity;
}

这段代码就是提前告诉渲染引擎 box 元素将要做几何变换和透明度变换操作,这时候渲染引擎会将该元素单独实现一帧,等这些变换发生时,渲染引擎会通过合成线程直接去处理变换,这些变换并没有涉及到主线程,这样就大大提升了渲染的效率。这也是 CSS 动画比 JavaScript 动画高效的原因。

所以,如果涉及到一些可以使用合成线程来处理 CSS 特效或者动画的情况,就尽量使用 will-change 来提前告诉渲染引擎,让它为该元素准备独立的层。但是凡事都有两面性,每当渲染引擎为一个元素准备一个独立层的时候,它占用的内存也会大大增加,因为从层树开始,后续每个阶段都会多一个层结构,这些都需要额外的内存,所以你需要恰当地使用 will-change。