← 返回题目列表

前端动画为什么推荐使用 transform 和 opacity?

中等 第 18 / 29 题 更新于 2026/07/29
前端性能动画优化合成层渲染流程

简化版

推荐用 transformopacity 做动画,是因为它们通常只需要合成线程处理,不必每帧重新布局或绘制。改变 widthheighttopleft 往往会触发布局和绘制,容易造成掉帧。动画优化要减少主线程工作、避免强制同步布局、合理使用合成层,并注意不要滥用 will-change

详细版

浏览器渲染大致经过样式计算、布局、绘制和合成。不同 CSS 属性会触发不同阶段。

动画属性可能触发性能风险
width/heightlayout + paint + composite
top/leftlayout + paint + composite
background-colorpaint + composite
transformcomposite
opacitycomposite
.bad {
  transition: left 300ms;
}

.good {
  transition: transform 300ms;
  transform: translateX(120px);
}

动画性能的重点不是“CSS 动画一定比 JS 快”,而是每一帧触发了哪些渲染阶段。

完整版教学

一、动画为什么容易掉帧

屏幕常见刷新率是 60Hz,一帧大约 16.7ms。如果页面每帧要做 JS、样式计算、布局、绘制和合成,只要超过预算就会掉帧。

动画是连续变化,任何一次变化成本过高,用户都会看到卡顿。尤其在低端手机上,主线程更容易被业务 JS、事件处理和渲染抢占。

二、浏览器渲染阶段要分清

浏览器处理视觉变化大致分为:

  • 样式计算:计算 CSS 规则结果。
  • 布局:计算元素几何位置和尺寸。
  • 绘制:把元素画到图层位图。
  • 合成:把多个图层组合到屏幕。

如果动画只影响合成,成本通常较低;如果每帧都触发布局和绘制,成本就高。

三、为什么 transform 和 opacity 更友好

transform 改变的是元素在合成阶段的位置、缩放或旋转;opacity 改变透明度。它们通常可以在合成线程处理,不必重新计算其他元素布局。

这也是为什么移动、缩放、淡入淡出动画常用它们。

.fade-slide-enter {
  opacity: 0;
  transform: translateY(8px);
}

.fade-slide-enter-active {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 200ms ease, transform 200ms ease;
}

四、合成层不是越多越好

浏览器可能把某些元素提升为独立合成层。独立层可以减少重绘范围,但每个层都占内存,也会增加合成成本。

will-change 可以提前提示浏览器优化某个属性,但不能全站乱加。

.dragging {
  will-change: transform;
}

拖拽开始前加,结束后移除,比长期挂着更稳。

五、JS 动画和 CSS 动画怎么选

CSS 动画适合简单状态过渡,声明式、成本低。JS 动画适合复杂编排、物理效果、手势跟随和可中断动画。

JS 动画要用 requestAnimationFrame,不要用 setInterval 硬刷。

function step() {
  element.style.transform = `translateX(${x}px)`
  requestAnimationFrame(step)
}
requestAnimationFrame(step)

六、如何排查动画卡顿

可以用 Chrome DevTools Performance 面板录制,观察是否出现长任务、Layout、Paint 过多。Rendering 面板也可以打开 paint flashing 查看重绘区域。

如果每帧都有 Layout,多半是改了布局属性或读写布局交错。如果 Paint 很重,可能是阴影、滤镜、大面积渐变或复杂 SVG。

七、常见误区与追问

  • 误区:CSS 动画一定比 JS 动画快。 真正关键是触发哪些渲染阶段,JS 用 rAF 且只改 transform 也可以很顺。
  • 误区:will-change 应该提前给所有动画元素加上。 滥用会增加内存和合成成本,应该短期、精准使用。
  • 误区:只要用 transform 就完全没有成本。 合成也有成本,大量图层、大图片和滤镜仍可能卡。
  • 追问:为什么 top/left 动画容易慢? 它可能改变布局位置,导致布局和绘制反复发生。
  • 追问:如何判断动画触发了 paint? 用 DevTools Performance 或 paint flashing 观察绘制区域。
  • 追问:拖拽动画用什么实现? 常用 rAF 加 transform,并避免在每帧读写布局混杂。

八、加强记忆

动画优化记住“三少”:少布局、少绘制、少长期合成层。移动和淡入淡出优先 transformopacity,复杂动画用 rAF 控制节奏,will-change 只在必要窗口使用。