{T}

分层与合成机制:为什么 CSS 动画比 JavaScript 高效

概述

浏览器的分层与合成机制是现代渲染性能优化的核心。通过将页面分解为独立图层,在合成线程上执行变换操作,CSS 动画可以完全绕过主线程,实现 60fps 的流畅效果。本文将系统阐述分层、分块、合成的完整机制。


1 帧、帧率与 VSync

1.1 帧的生成

显示器以固定频率(通常 60Hz)刷新,每次刷新从显卡前缓冲区读取一帧。渲染引擎需在每次 VSync 信号前生成新帧,否则出现丢帧(卡顿)。

单帧时间预算:1000ms / 60fps ≈ 16.67ms

1.2 三种帧生成方式

方式涉及阶段线程性能
重排样式+布局+绘制+合成主线程最差
重绘绘制+合成主线程中等
合成仅合成合成线程最优

2 分层机制

2.1 图层提升条件

条件CSS 属性/元素
3D 变换transform: translateZ(0)
will-changewill-change: transform, opacity
CSS 滤镜filter, backdrop-filter
透明度动画opacity < 1 + will-change
溢出剪裁overflow: hidden/auto
position: fixed固定定位
<video>硬件加速视频
<canvas>Canvas/WebGL/WebGPU

2.2 will-change 的使用

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

will-change 告诉渲染引擎提前为元素创建独立图层。注意事项

  • 不要对所有元素设置 will-change——每个图层消耗额外内存
  • 动画结束后移除 will-change——释放资源
  • 优先使用 CSS @starting-style(2023+)替代

3 分块与光栅化

3.1 分块策略

合成线程将每个图层分割为 256×256 或 512×512 的图块(Tile),优先光栅化视口附近的图块。

3.2 GPU 光栅化

光栅化(将绘制指令转换为位图)在 GPU 进程中执行,利用 GPU 并行计算能力加速。

3.3 低分辨率先行

首次合成时使用低分辨率(1/2 或 1/4)位图快速显示,后续替换为全分辨率。


4 CSS 动画 vs JavaScript 动画

4.1 CSS transform/opacity 动画

css
.box {
    will-change: transform;
    transition: transform 0.3s ease;
}
.box:hover {
    transform: translateX(100px);
}

执行路径:仅触发合成阶段,在合成线程执行,不阻塞主线程。

4.2 JavaScript 动画

javascript
function animate() {
    element.style.left = `${currentPos++}px`;
    requestAnimationFrame(animate);
}

执行路径:修改 left 属性触发重排(布局+绘制+合成),在主线程执行,阻塞其他任务。

4.3 性能对比

图表渲染中…

4.4 最佳实践

场景推荐方案
简单位移/缩放/旋转CSS transform
透明度渐变CSS opacity
复杂时间线Web Animations API
物理模拟/粒子requestAnimationFrame + transform
滚动驱动动画CSS animation-timeline: scroll()

5 总结

分层与合成机制是 Chrome 渲染性能优化的基石。CSS transform/opacity 动画仅触发合成阶段,在合成线程执行,不占用主线程,因此比 JavaScript 动画高效。合理使用 will-change 提前创建图层,但需注意内存开销。


参考文献

  1. Chrome Blog: RenderingNG
  2. web.dev: animating efficiently
  3. MDN: will-change