掉帧主因是动画触发重排或重绘,被迫切回CPU渲染;高危属性包括top/left/width/height/margin/padding/background-color/box-shadow/border-radius/filter,混用即降级,且不报错;仅transform和opacity可安全触发GPU合成。

掉帧不是因为“没开硬件加速”,而是动画本身触发了重排或重绘,浏览器被迫切回 CPU 渲染路径——尤其在 Android 5–7 WebView 和 iOS Safari 中,只要混入一个非合成属性,整段动画就默默降级。
哪些 CSS 属性一动就掉帧
top、left、width、height、margin、padding、background-color、box-shadow、border-radius、filter(除简单 blur(1px)、grayscale(0.5) 外)都会触发 layout 或 paint。它们不是“慢一点”,而是直接让主线程卡死一帧或多帧。
-
transition: left 0.3s→ 整条过渡退化为 CPU 渲染,哪怕只写这一行 - @keyframes 里加一行
box-shadow: 0 2px 4px rgba(0,0,0,0.1)→ 整段动画掉帧,浏览器不报错也不警告 -
transform: translate3d(0, 0, 0)单独写没问题,但若和top: 10px同时存在,浏览器优先走重排路径
为什么 transform 不等于 GPU 加速
用了 transform 只是“有资格”进合成层,不代表真被提升。是否生成绿色图层(Chrome DevTools → Rendering → Layer Borders),取决于父容器是否设了 overflow: hidden、filter: blur(1px),或 JS 是否在动画中调了 getBoundingClientRect()。
- 父元素
overflow: hidden会抑制子元素提层,即使子元素写了will-change: transform - 动画过程中执行一次
el.offsetHeight,就会强制同步 layout,直接破防 -
transform: translateZ(0)在部分 Android 5.1 WebView 中不稳定,translate3d(0, 0, 0)更可靠
will-change 是提示,不是保险丝
静态写 .item { will-change: transform; } 等于给每个列表项预分配 GPU 内存,在低端安卓机上极易 OOM 卡死。它必须动态加、及时删。
立即学习“前端免费学习笔记(深入)”;
- 加的时机:
touchstart或mouseenter后、animationclass 应用前那一帧 - 删的时机:监听
animationend(不是transitionend),并同步清除;iOS Safari 对 ≤16ms 动画常不触发该事件,必须加setTimeout(() => el.classList.remove('is-animating'), duration + 100)兜底 - 绝对不要对滚动区域内的列表项批量加
will-change,哪怕只是 hover 效果
验证是否真走 GPU 渲染
别猜,用 Chrome DevTools 实测。打开 Rendering 面板 → 勾选 “Layer Borders”。只有带绿色边框的元素才是被真正提升的合成层。
- 没绿框?先检查父容器有没有
overflow: hidden或filter干扰 - 有绿框但还是卡?看是否在动画中读取了
offsetHeight、getComputedStyle()等布局信息 - 绿框过大或过多?说明图层尺寸失控,比如给全屏弹窗加了
backdrop-filter: blur(10px),会拖垮合成器
真正决定是否掉帧的,从来不是“有没有加 translate3d”,而是“有没有让浏览器少干一次 layout”。删掉所有 top/left、禁用所有 animationend 依赖、用绿框验证每一帧——这些事做起来琐碎,但漏掉任何一项,都可能在某台 Android 6 手机上突然卡住。


















