transform 和 opacity 动画更流畅,因它们走 gpu 合成层,不触发重排与重绘;而 width、height 等属性变化会强制重排,导致帧率下降。

为什么 transform 和 opacity 过渡更流畅
浏览器对 transform 和 opacity 的动画能走合成层(compositor layer),不触发重排(reflow)和重绘(repaint),只走 GPU 合成。而 width、height、top、left、margin、padding 等属性变化会强制触发重排,尤其在中低端设备或复杂 DOM 下,帧率骤降明显。
常见卡顿现象包括:过渡开始瞬间掉帧、滚动时动画 stutter、多元素同时过渡时明显拖慢。
- 优先用
transform: translateX(100px)替代left: 100px - 用
transform: scale(1.2)替代width/height变更 - 透明度切换统一用
opacity,别用visibility或display控制显隐
如何检测哪些属性触发了重排
Chrome DevTools 的 Rendering 面板(Cmd+Shift+P → 输入 “Rendering”)开启 Paint flashing 和 Layout Shift Regions,可直观看到重绘/重排区域。更精准的方式是录制 Performance 面板的动画帧,查看 Layout 和 Recalculate Style 是否频繁出现。
- 避免在
transition中写all,必须明确列出仅需过渡的属性,例如:transition: transform 0.3s ease, opacity 0.3s ease - 不要在 JS 中频繁读取
offsetWidth、getBoundingClientRect()等触发同步布局计算的 API,否则会强制刷新并阻塞后续动画 - 给动画元素加
will-change: transform(仅在必要时,且记得过渡结束后移除)可提前提示浏览器提升图层,但滥用会导致内存占用上升
transform: translateZ(0) 还管用吗
这个技巧曾用于强制启用硬件加速,但在现代 Chrome/Firefox/Safari 中已基本失效,甚至可能引发额外图层合并开销。目前更可靠的做法是使用 transform: translate3d(0, 0, 0) 或直接 transform: translateX(0) —— 只要 transform 值非空,浏览器通常就会创建独立合成层。
立即学习“前端免费学习笔记(深入)”;
- 慎用
translateZ(0):它在某些 Android WebView 中仍有效,但桌面端无实质收益 - 避免给大量元素同时加
will-change或transform3d,图层过多反而降低性能 - 用
contain: paint可限制重绘范围,适合动画容器(如轮播图外层),但 IE/Edge 不支持
JS 触发动画时容易忽略的陷阱
即使 CSS 属性本身是“可合成”的,JS 调用方式不对也会打断流水线。比如在 requestAnimationFrame 外直接改 style.transform,或在事件回调里连续多次设置样式。
const el = document.querySelector('.box');
// ❌ 错误:触发同步布局
el.style.left = '100px';
console.log(el.offsetWidth); // 强制回流
// ✅ 正确:批量变更 + 使用 transform
el.style.transform = 'translateX(100px)';
// 若需链式动画,用 CSS @keyframes 或 Web Animations API
- 所有样式变更尽量在单次
requestAnimationFrame回调中完成 - 避免在
scroll或mousemove中直接操作style,应节流 +transform更新 - 动画结束用
transitionend事件而非setTimeout,后者无法对齐实际渲染帧
margin-top: 2px 动画,和隐藏在 forEach 循环里的十几次 offsetHeight 读取。











