transform和opacity是仅有的两个能稳住60fps的过渡属性;left/top/width等会触发重排,导致主线程阻塞;需用Layer Borders验证合成层、动态控制will-change、慎用backface-visibility。

低端安卓机(尤其是 Android 5–7 WebView)上 transition 掉帧、撕裂,90% 不是没开 GPU 加速,而是用了会触发重排的属性、混入了非合成属性,或图层管理失控——transform 和 opacity 是仅有的两个能稳住 60fps 的过渡属性,其他全算风险项。
为什么 left/top/width 动画必掉帧
改 left 或 top 会强制触发重排(reflow),浏览器得在主线程里重新计算位置、尺寸、换行、文本流……一次重排实测耗时 3–8ms,远超 16.67ms 的帧间隔。Android 5–7 WebView 在一帧内反复重排,主线程直接堵死。
-
transform: translateX(100px)只挪图层,由 GPU 合成线程处理,不碰布局、不重绘、不阻塞主线程 - 错:
transition: left 0.3s→ 整条过渡降级为 CPU 渲染 - 对:
transition: transform 0.3s,且动画中只改transform,不混top、margin、width - 绝对不要写
transition: all 0.3s——它会把后续任意样式变更(比如color)也拉进过渡,悄悄破防
如何验证 GPU 加速是否真生效
写了 transform、加了 will-change,不代表真加速。关键看浏览器是否建了独立合成层。
- Chrome DevTools → Rendering 面板 → 勾选 “Layer Borders”:有绿色边框的元素才是被提升的图层
- 没绿框?检查父容器是否设了
overflow: hidden或filter: blur(1px)——这些会抑制子元素提层 - 动画过程中禁用所有
getBoundingClientRect()、offsetHeight、getComputedStyle(),哪怕只调一次,也会强制同步 layout,直接破防
backface-visibility: hidden 是闪屏第一道防线
安卓低版本(2.2–4.4)WebView 合成层管理失控,常导致首帧空白、图层撕裂或背面意外显示。这不是性能问题,是图层状态错乱。
立即学习“前端免费学习笔记(深入)”;
- 优先加全前缀:
-webkit-backface-visibility: hidden、-moz-backface-visibility: hidden、backface-visibility: hidden - 必须加在动画元素本身上(如
.slide),不是父容器 - 在 Android 4.4–5.1 中,该属性反而可能抑制图层创建,此时要禁用它,改用 JS 动态注入
transform: translate3d(0,0,0)+will-change: transform,动画结束立即移除 - 别和
filter、mask、overflow: hidden共用——旧 WebView 里它们会强制降级为 CPU 渲染
will-change 必须动态控制,静态写等于埋雷
will-change 是提示,不是开关;浏览器是否真建层、何时回收,由实现决定,不可控。
- 静态写
.item { will-change: transform; }等于给一屏 20 个列表项各占一块 GPU 内存,低端安卓机极易 OOM 卡死 - 加的时机:用户真实交互瞬间,比如
touchstart触发后、动画 class 应用前那一帧 - 删的时机:监听
transitionend并同步清除;iOS Safari 对 ≤16ms 动画可能不触发该事件,务必加兜底:setTimeout(() => el.classList.remove('is-animating'), duration + 100) - 绝对不要写
* { will-change: transform; }或批量加到列表项上
真正顺滑的前提,不是“加了什么”,而是“没动什么”——别在动画过程中读取布局、别混非合成属性、别让图层失控。低端机上,克制比技巧更重要。


















