根本原因是动画属性选错、图层管理失控或被降级回cpu渲染;top/left触发重排耗时3–8ms,远超16.67ms帧间隔,而transform/opacity才是安全组合,其他属性如box-shadow、filter、width/height等均会导致静默掉帧。

根本原因不是“没开硬件加速”,而是动画属性选错了、图层管理失控了、或者被浏览器悄悄降级回 CPU 渲染——低端安卓机(尤其是 Android 5–7 WebView 和旧版 Chrome)对重排和重绘极其敏感,一帧内多一次 layout 就可能卡死。
为什么 top/left 动画必掉帧
这两个是布局属性,每次变更都会触发重排(reflow),浏览器必须在主线程里重新计算尺寸、位置、文本流、换行……实测单次耗时 3–8ms,远超 16.67ms 的 60fps 帧间隔。低端机上动画一跑,主线程直接堵死。
- 错:
@keyframes slide { to { left: 200px; } }→ 整段降级为 CPU 渲染 - 对:
@keyframes slide { to { transform: translateX(200px); } }→ 真走 GPU 合成 - 混用危险:
transform: translateX(100px); left: 50px;→ 浏览器放弃加速,退回软件渲染
哪些属性会让动画“静默掉帧”
浏览器不会报错,但只要 @keyframes 或 transition 里混入一个非合成属性,整条动画就退回到 CPU 渲染。常见隐形杀手:
-
box-shadow:模糊计算强制 CPU 绘制,blur-radius每 +2px,显存占用平方级增长 -
filter: blur():低端 WebView 中几乎必掉帧;哪怕blur(1px)也常触发全层重绘 -
background-color、color、border-radius:触发重绘(repaint),高 DPI 屏上更明显 -
width/height:改盒模型尺寸,必然重排
安全组合只有两个:transform 和 opacity。其他全删。
will-change 和 translateZ(0) 不是保险丝
静态写 .item { will-change: transform; } 等于给每个元素提前建 GPU 图层,在一屏 20 个列表项的场景下,低端安卓机极易 OOM 卡死。
- 加的时机:用户交互触发瞬间(如
touchstart),或requestAnimationFrame第一帧内 - 删的时机:监听
animationend后立刻设el.style.willChange = 'auto' - iOS Safari 对 ≤16ms 动画可能不触发事件,必须加兜底:
setTimeout(() => el.style.willChange = 'auto', duration + 100) - 绝对不要写
* { will-change: transform; }或批量加到列表项上
怎么确认 GPU 加速真起了作用
写了 transform 不等于就加速了。关键看浏览器是否真建了独立合成层。
- Chrome DevTools → Rendering 面板 → 勾选 “Layer Borders”:有绿色边框的元素才是被提升的图层
- 没绿框?检查父容器是否设了
overflow: hidden或filter: blur(1px)—— 这些会抑制子元素提层 - 动画过程中禁用所有
getBoundingClientRect()、offsetHeight、getComputedStyle(),哪怕只调一次也会强制同步 layout,直接破防
真正卡住的,往往不是“要不要用 CSS 动画”,而是有没有把动画限定在 GPU 可加速的属性上、有没有避开 WebView 的解析盲区、有没有让动画和 JS 状态严格对齐。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











