CSS动画流畅的关键是使用transform和opacity,因其由合成线程处理,跳过Layout与Paint;避免left、top等触发重排的属性,合理动态控制will-change,禁用translateZ(0)伪加速。

只用 transform 和 opacity 做 transition
浏览器对 transform(如 translateX、scale、rotate)和 opacity 的变化能直接交由合成线程处理,跳过 Layout 和 Paint 阶段。这是实现稳定 60fps 的前提。
常见错误是写成:left: 100px; 或 margin-top: 20px;——这些会强制重排,哪怕加了 will-change 也救不回来。
- ✅ 正确写法:
transform: translateX(100px);+transition: transform 0.3s ease; - ✅ 淡入淡出:
opacity: 0;→opacity: 1;,搭配transition: opacity 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); - ❌ 别混用:比如
transition: transform 0.3s, opacity 0.3s, left 0.3s;—— 只要含一个非加速属性,整条 transition 就降级到主线程渲染
动态控制 will-change,不写死在 CSS 里
will-change 是提示信号,不是开关。静态写在 class 里(如 .slide { will-change: transform; })会导致图层长期驻留,浪费 GPU 内存,尤其在列表或滚动区域极易引发内存抖动。
真实场景中应 JS 动态控制:
立即学习“前端免费学习笔记(深入)”;
- 动画开始前(例如点击后):
el.style.willChange = 'transform'; - 动画结束后(监听
transitionend或用setTimeout延迟 1 帧):el.style.willChange = 'auto'; - Vue 场景下,可在
@before-enter钩子设,@after-leave钩子清;React 可结合useLayoutEffect+requestAnimationFrame
避免 translateZ(0) 这类“伪加速”写法
transform: translateZ(0) 或 transform: translate3d(0, 0, 0) 确实能强制创建合成层,但它本身不参与动画逻辑。如果动画目标属性仍是 top 或 width,GPU 依然不接手——只是多建了个图层,还占显存。
真正起效的前提只有一个:动画属性本身属于 GPU 友好集(transform/opacity)。
- ✅ 有效组合:
transform: translateX(0) scale(1);→transform: translateX(100px) scale(1.2); - ❌ 无效组合:
transform: translateZ(0);+top: 100px;——top仍触发重排 - ⚠️ 注意:iOS Safari 对
will-change支持不稳定,部分版本甚至导致闪烁,优先靠transform本身触发图层,而非依赖提示
用 DevTools 验证是否真走 GPU 合成
肉眼判断不准。必须打开 Chrome DevTools → Rendering 面板:
- 勾选
Layer borders:动画元素周围应出现清晰的绿色边框(表示独立合成层) - 勾选
Paint flashing:动画过程中仅内容变化处微闪,大面积持续闪烁说明仍在 Paint 阶段工作 - 录制 Performance 面板:看每帧耗时是否稳定 ≤16.67ms,且主线程大部分时间处于空闲状态
如果看到某帧主线程突然拉升到 30ms 以上,大概率是 JS 中读取了 offsetHeight、getComputedStyle 或触发了强制同步布局——这类问题藏得深,但一抓一个准。


















