卡顿主因是浏览器每帧计算过多,非动画时长问题;应优先用 will-change + transform/opacity 实现60fps,避免触发layout/paint,并通过FPS meter和Paint flashing定位真实性能瓶颈。

为什么改了 animation-duration 还卡顿
单纯缩短 animation-duration 或调小 transition-duration 并不能保证流畅,因为卡顿往往不是“时间太长”,而是浏览器在每一帧里做了太多事。比如动画属性触发了 layout(重排)或 paint(重绘),导致掉帧。常见诱因包括:对 width、height、top/left、margin 等非合成属性做动画,或者元素没开启硬件加速。
优先用 will-change + transform 实现 60fps 动画
浏览器对 transform 和 opacity 的动画天然走合成层(compositor thread),不触发 layout/paint,是唯一能稳定跑满 60fps 的组合。要让这个机制生效,得配合 will-change 提前声明,但别滥用:
-
will-change: transform只应在动画开始前 1–2 帧设置,动画结束及时移除(可用animationend事件) - 避免写
will-change: all或长期挂载在静态元素上,会浪费内存、拖慢初始渲染 - 用
transform: translateZ(0)或transform: translate3d(0, 0, 0)强制升层,适合老版本 Safari/Android Webview
示例(平滑移动):
.box {
transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.box:hover {
transform: translateX(20px);
}
transition 和 animation 在性能上真有区别吗
没有本质区别——底层都走 CSS 动画管线。但实际使用中,transition 更易控制触发时机(比如 hover、class 切换),而 animation 适合复杂时序(keyframes 多段变化)。容易踩的坑有:
立即学习“前端免费学习笔记(深入)”;
-
transition对未声明初始值的属性无效(比如没设transform: translateX(0),直接 hover 加translateX(20px)会跳变) -
animation若用animation-fill-mode: forwards,结束后样式被锁定,后续 JS 修改transform可能被覆盖,需注意层叠顺序 - 多个
transition同时作用于同一元素时,会按属性名逐个匹配,不是“覆盖”,容易漏写某项(如只写了transition: transform 0.3s,但忘了opacity)
检查是否真掉帧:别只看 duration 数值
打开 Chrome DevTools → Rendering → 勾选 “FPS meter” 和 “Paint flashing”。真实卡顿表现为:
- FPS 长期低于 50,且绿色柱状图频繁断续
- Paint flashing 区域过大(尤其动画区域外也高亮),说明重绘范围失控
- Performance 面板录制后看主线程火焰图,发现
Layout或Update Layer Tree占比过高
此时该做的不是再调 duration,而是:删掉所有非 transform/opacity 动画、确保动画元素为独立图层(contain: paint 或 isolation: isolate)、避免父容器频繁 resize 或 scroll 触发子元素重算。
真正影响流畅度的,从来不是 0.3s 还是 0.2s,而是那一帧里浏览器有没有被逼着重新计算整个布局树。









