触发布局(重排)的CSS属性包括width、height、top、left、margin、padding、border、font-size等;动画中应仅用transform和opacity,避免使用会触发重排的属性。

哪些 CSS 属性触发布局(重排)?
修改 width、height、top、left、margin、padding、border、font-size 等会改变元素几何尺寸或位置的属性,都会触发浏览器重新计算布局(reflow),开销极大。
动画中应完全避免用这些属性做关键帧变化。比如用 left 实现位移,哪怕加了 transform: translateX(0) 也拦不住重排——因为 left 本身已触发布局计算。
- ✅ 推荐:只用
transform(translate、scale、rotate)和opacity - ❌ 避免:
top/left、margin、width、height、box-shadow(部分情况)、background-position(非硬件加速时)
如何确保 transform 动画走 GPU 加速?
不是所有 transform 都自动上 GPU。浏览器只对「合成层」(compositing layer)启用硬件加速,而合成层需满足特定条件才会被创建。
常见误操作是给元素加 transform: translateZ(0) 或 will-change: transform 就以为万事大吉——但若父容器有 overflow: hidden、filter、mask 等,可能压制子元素的合成层提升。
立即学习“前端免费学习笔记(深入)”;
- ✅ 检查是否真有合成层:Chrome DevTools → Layers 面板,看动画元素是否有独立图层
- ✅ 更稳妥写法:
transform: translate3d(0, 0, 0)(比translateZ(0)兼容性略好) - ⚠️
will-change: transform不要滥用:提前声明会常驻合成层,内存占用上升;仅在动画开始前 1–2 帧设置,结束后设回auto
为什么 animation-timing-function 影响帧率?
cubic-bezier(0.42, 0, 0.58, 1)(即 ease-in-out)这类缓动函数本身不卡顿,但若关键帧中混入非合成属性(如 background-color),浏览器就无法预测下一帧样式,被迫降级为软件渲染,导致掉帧。
更隐蔽的问题是:多个 @keyframes 同时运行,且都含 opacity + transform,看似合规,但如果它们共享同一个父容器且该容器有 filter: blur(2px),整个容器会被强制单层绘制,GPU 加速失效。
- ✅ 把高频动画元素提级到独立容器,避免受父级
filter、clip-path、transform(非 identity)影响 - ✅ 用
prefers-reduced-motion媒体查询兜底,对系统开启“减少动画”的用户直接禁用非必要动画 - ⚠️ 不要用
animation: name 0.3s ease-in-out+transition: opacity 0.3s叠加控制同一元素——时机难同步,易触发意外重绘
用 will-change 的真实代价是什么?
will-change 不是开关,而是提示浏览器“这个元素接下来很可能变”,它会提前分配纹理内存、创建合成层。但若提示后实际没变,或变化频率极低(比如 hover 动画只持续 200ms),这部分资源就白占了。
移动端尤其敏感:iOS Safari 对合成层数量有限制,过多 will-change 可能导致其他本该加速的动画被挤出 GPU。
- ✅ 正确用法:在事件触发前(如
mouseenter)动态加style.willChange = 'transform, opacity',动画结束后的animationend事件里立刻清空 - ❌ 错误用法:全局 CSS 写
* { will-change: transform },或对列表项每个都静态声明 - ⚠️ 替代方案:多数场景下,
transform: translate3d(0, 0, 0)已足够触发提升,比will-change更轻量可控
真正卡顿往往不出现在动画本身,而出现在动画开始前那一帧——比如 JS 修改了 class 触发样式重算,又紧接着读取 offsetHeight,造成强制同步布局。优化动画,得先守住 JS 和 CSS 交界处的那条线。











