transform和opacity是仅有的两个能稳住60fps的合成属性,其他任何混入都直接降级回CPU渲染;top/left触发重排导致主线程堵塞,而translate仅挪图层由GPU合成,不触布局不重绘。

移动端 CSS 动画卡顿,90% 不是因为没开 GPU 加速,而是开了但用错了属性、时机或方式——transform 和 opacity 是仅有的两个能稳住 60fps 的合成属性,其他任何混入都直接降级回 CPU 渲染。
为什么 top/left 动画必卡
改 top 或 left 会强制触发重排(reflow),浏览器得在主线程里重新计算位置、尺寸、换行、文本流……低端安卓机(尤其 Android 5–7 WebView)一帧内反复重排,主线程瞬间堵死。而 transform: translateX(100px) 只挪图层,GPU 合成,不碰布局也不重绘。
- 错:
transition: left 0.3s→ 整条过渡降级为 CPU 渲染 - 对:
transition: transform 0.3s,且只改transform,不混top、margin、width -
@keyframes里哪怕加一个box-shadow或background-color,整段动画就默默掉帧 - 别写
transition: all 0.3s—— 它是定时炸弹,某次意外改了color就破防
如何正确使用 will-change
will-change 是提示,不是开关;浏览器是否真建层、何时回收,由实现决定,不可控。静态写 .item { will-change: transform; } 等于给一屏 20 个列表项各占一块 GPU 内存,低端安卓机极易 OOM 卡死。
- 加的时机:用户真实交互瞬间,比如
touchstart触发后、动画 class 应用前那一帧 - 推荐用 class 控制:
.card.is-animating { will-change: transform; } - 删的时机:必须监听
animationend(不是transitionend),并同步清除;iOS Safari 对 ≤16ms 动画可能不触发该事件,务必加兜底:setTimeout(() => el.classList.remove('is-animating'), duration + 100) -
will-change: contents或scroll-position几乎无效,且前者会强制子树全升层,极易图层爆炸
验证 GPU 加速是否真实启用
写了 transform、加了 will-change,不代表真加速。关键看浏览器是否真建了独立合成层。
立即学习“前端免费学习笔记(深入)”;
- Chrome DevTools → Rendering 面板 → 勾选 “Layer Borders”:有绿色边框的元素才是被提升的图层
- 没绿框?检查父容器是否设了
overflow: hidden或filter: blur(1px)—— 这些会抑制子元素提层 - 避免在动画元素上动态改
box-shadow或filter,它们可能破坏图层合并,退回到 CPU 渲染 - 动画过程中禁用所有
getBoundingClientRect()、offsetHeight、getComputedStyle()—— 即使只调一次,也会强制同步 layout,直接破防
真正顺滑的前提,不是“加了什么”,而是“全程只走合成阶段”:不触发布局、不触发重绘、不偷偷读取任何布局信息——这些细节一旦漏掉,再标准的 transform 也救不回来。


















