transform和opacity是仅有的两个能稳住60fps的合成属性,混入left、width或background-color等会触发重排重绘,导致降级至CPU渲染;正确做法是仅用transform与opacity驱动动画,并在交互瞬间按需启用will-change或translateZ(0),动画结束后及时清理。

掉帧不是因为“GPU没开”,而是动画属性触发了主线程重排重绘——transform 和 opacity 是仅有的两个能稳住 60fps 的合成属性,混入 left、width 或 background-color 就直接退化到 CPU 渲染。
为什么 transition: left 会卡,而 transition: transform 不卡
浏览器对 left 的每次变更都要重新计算布局(reflow)→ 重绘像素(paint)→ 合成图层(composite),三步全走,低端安卓机一帧内反复触发就堵死主线程;transform 只走最后一步,前提是元素已提升为独立合成层。
-
transition: left 0.3s→ 整条过渡降级,必掉帧 -
transition: transform 0.3s, opacity 0.3s→ 正确,但必须确保 JS 或 class 切换时只改这两个属性 - 别用
transition: all 0.3s:它像定时炸弹,某次改了margin就破防 - 动画中哪怕加一个动态
box-shadow或filter: blur(0),整段就悄悄退化
如何真正开启 GPU 加速(不是写个 translateZ 就完事)
写 transform: translateZ(0) 或 translate3d(0, 0, 0) 只是“发信号”让浏览器建层,不等于动画就加速了。关键在三点:时机、范围、清理。
- 只对**正在动画或即将动画**的元素临时启用,比如 touchstart 后、class 应用前那一帧加
will-change: transform - 避免全局写
* { will-change: transform; }或给列表项批量加——安卓低端机容易 OOM - 动画结束必须清除:
el.style.willChange = 'auto',iOS Safari 对短动画(≤16ms)可能不触发animationend,要加setTimeout(() => ..., duration + 100)兜底 -
translateZ(0)比will-change更稳,兼容性好,语义清晰;二者叠用没必要
怎么验证 GPU 加速真起了作用
不能靠“感觉流畅”,得看 Chrome DevTools 的真实数据:
立即学习“前端免费学习笔记(深入)”;
- 打开
Rendering面板 → 勾选Layer Borders:有绿色边框的元素才是被提升的图层 - 没绿框?检查父容器是否设了
overflow: hidden、filter: blur(1px)或opacity: 0.99——这些会抑制子元素提层 - 打开
Performance面板录制动画,火焰图里若只有Composite Layers块,没有Layout或Paint,才算成功卸载 - 启用
Rendering → Paint flashing:动画期间屏幕不该大面积绿色闪烁,否则仍在重绘
最易被忽略的是动画过程中的隐性退化:调一次 getBoundingClientRect()、改一次 z-index、滚动监听里频繁 toggle class 没节流——这些都会让图层反复创建销毁,GPU 加速形同虚设。


















