必须仅用transform和opacity做动画,因二者仅触发合成层操作、避开重排与重绘;width、height、top、left等属性会强制重排,必然卡顿。

直接用 transform 和 opacity,别碰 left、top、width、height —— 这不是建议,是硬性边界。
哪些CSS属性会触发重排(reflow)?
改 width、height、top、left、margin、padding、font-size、border,哪怕只动一个像素,浏览器就得重新计算整个布局树。这不是“可能卡”,而是“必然卡”。
- 常见错误现象:
element.style.left = x + 'px'放在scroll或mousemove里,页面立刻掉帧甚至冻结 - 即使加了
transform: translateX(0),只要同时写了left,重排照常触发——left本身已进入 layout 阶段 -
display: none比visibility: hidden更重:前者删布局,后者只跳过绘制;但两者都不该用于动画过渡
为什么只用 transform 和 opacity 能避开重排重绘?
因为它们属于合成层(compositing layer)属性,浏览器会把这类变化交给 GPU 单独处理,不干扰主渲染流程的 style → layout → paint 流水线。
- 前提是元素已提升为独立图层:可通过
transform: translate3d(0, 0, 0)或will-change: transform触发(但后者慎用) -
opacity动画不会触发重绘,但background-color会——哪怕只在关键帧里混入一次,整个动画就降级为 CPU 渲染 - 多个
@keyframes共享同一父容器时,若父级有filter、clip-path或非 identity 的transform,GPU 加速会被压制,所有子元素被迫合并在一层绘制
class 切换比内联 style 更安全的底层原因
直接写 element.style.left = '10px' 不仅污染内联样式,还会让浏览器无法批量合并样式计算——每次赋值都可能触发一次重排。
立即学习“前端免费学习笔记(深入)”;
- 10 次
el.classList.add('moving')+el.classList.remove('idle'),大概率只触发 1 次重排;10 次el.style.left = ...基本就是 10 次重排 - CSS 文件里的规则可被浏览器缓存、复用、预编译;JS 拼接的
style.cssText或逐个设style.xxx,绕过了这些优化 - 需要动态值时,优先用 CSS Custom Properties:
element.style.setProperty('--offset-x', '10px'),再在 CSS 里写transform: translateX(var(--offset-x))
will-change 的真实代价和正确用法
will-change 不是性能开关,它是内存预分配指令:浏览器看到它,就会提前创建纹理、分配显存、升层。滥用等于主动吃内存。
- 不要全局加
will-change: transform;只在动画开始前 1–2 帧设置,结束后立刻设回auto - Chrome DevTools → Layers 面板才是唯一验证方式:没看到独立图层,说明提升失败(常见于父容器有
overflow: hidden或filter) -
translateZ(0)兼容性略差,translate3d(0, 0, 0)是更稳妥的选择;但二者本质相同,都不是“魔法前缀”
真正难的不是知道该用 transform,而是当产品需求要求“从左侧滑入+背景渐变+带模糊阴影”时,得意识到这三者不能塞进同一个动画——filter: blur() 和 background 变化会直接废掉 GPU 加速,必须拆到不同层级或妥协其中一项。



















