掉帧主因是使用top/left/width等触发重排的属性,而非硬件加速未开启;应仅用transform和opacity做动画,因其走gpu合成线程、不触发重排重绘,混入任何非合成属性或同步读取布局信息均会导致降级。

掉帧不是因为“没开硬件加速”,而是你用了 top、left、width 这类会触发重排的属性——浏览器被迫在主线程反复计算布局,低端安卓机(尤其是 Android 5–7 WebView)直接卡死。
为什么 top/left 一动就掉到 20fps?
改 top 或 left 会强制触发重排(reflow):浏览器得重新算位置、尺寸、换行、文本流……这个过程纯 CPU 跑,且必须串行。低端机调度弱,一帧内反复重排,主线程瞬间堵死,动画就断成幻灯片。
-
transform: translateX(100px)不碰布局,只挪图层,GPU 合成线程直接处理 - 哪怕只是临时调试加了
left: 0,只要和transform共存,浏览器就会放弃图层优化 - 实测 MT6737(Android 6.0)上
top: 20px动画帧率常跌破 20fps
transition 里混了非合成属性就全废
浏览器不会报错,但帧率会默默掉下去。只要过渡属性里带一个非合成项,整条流水线就降级回 CPU 渲染。
- 错:
transition: left 0.3s, opacity 0.3s→left拖垮全部 - 对:
transition: transform 0.3s, opacity 0.3s→ 只留安全组合 - 绝对不要写
transition: all 0.3s—— 某次意外改了color就破防 - 缩放用
transform: scale(0.9),别碰width/height
will-change 是定时器,不是保险丝
静态写 .item { will-change: transform; } 等于让每个元素永远占一块 GPU 图层,内存吃紧时安卓低端机直接 OOM 卡死。
- 加的时机:动画开始前 1–2 帧,用 JS 动态设
el.style.willChange = 'transform' - 清的时机:监听
transitionend后立刻设el.style.willChange = 'auto' - iOS Safari 对 ≤16ms 动画可能不触发事件,必须加兜底:
setTimeout(() => el.style.willChange = 'auto', duration + 100) - 绝对别对列表项批量设:
.list-item { will-change: transform; }
最常被忽略的掉帧元凶:动画中偷偷读布局
哪怕只调一次 getBoundingClientRect()、offsetHeight 或 getComputedStyle(),都会强制同步 layout,直接破防。
- 动画过程中禁用所有
offsetTop、clientWidth、getComputedStyle() - 如果必须读,把操作提前到动画开始前,或延后到
animationend之后 - 验证是否真加速:Chrome DevTools → Rendering → Layer Borders,有绿框才算真提层
真正顺滑的前提,不是“加了什么”,而是“全程只走合成阶段”:不触发布局、不触发重绘、不偷偷读取任何布局信息——漏掉任意一点,再标准的 transform 也救不回来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











