答案是只对transform和opacity做transition,因其可走GPU合成层、不触发重排;left/top/width/height等属性会强制Layout→Paint全流程,导致卡顿。

只对 transform 和 opacity 做 transition
浏览器对 transform(如 translateX()、scale())和 opacity 的变化能直接走合成层,不触发重排(reflow),只走重绘(repaint)甚至纯合成(composite)。而 left、top、width、height、margin 这类属性一变,浏览器就得重新计算整个布局树。
常见错误写法:.item { transition: left 0.3s; } .item.active { left: 20px; } —— 看似简单,实则每帧都在重排。
正确替换方式:
- 位移:用
transform: translateX(20px)替代left: 20px - 缩放:用
transform: scale(1.1)替代width: 110%; height: 110% - 显隐:用
opacity: 0+visibility: hidden(配合pointer-events: none)替代display: none
避免在 transition 中混用布局属性和合成属性
一旦你在同一个元素上同时写了 transform 和 left,或者一边用 margin-top 一边用 transform: translateY(),浏览器可能丢弃部分声明、或强制降级到软件渲染——尤其当 transform 被覆盖时,动画就退回到重排模式。
立即学习“前端免费学习笔记(深入)”;
检查点:
- 删掉所有非必要定位属性(
top/left/right/bottom)——它们和transform不是互补关系,而是冲突源 - 确保
transition声明里只含transform、opacity、color(仅限小范围文字)等安全项,别写all - 用 DevTools 的 “Layers” 面板确认该元素是否生成了独立图层;没生成,说明仍卡在主线程跑 layout
will-change 不是常开开关,而是临时预告
will-change: transform 的作用不是“开启加速”,而是告诉浏览器:“接下来这个元素大概率要动,提前分配图层资源”。但它长期开着,等于让 GPU 持续盯一个静止元素,内存占用涨、其他动画反而卡。
实操建议:
- JS 动画开始前加:
el.style.willChange = 'transform' - 动画结束回调(如
transitionend)里立刻清除:el.style.willChange = 'auto' - 绝对不要写
will-change: all,也别对body或滚动容器设它 - Chrome 110+、Safari 16.4+ 已对
transform自动启用合成层,translateZ(0)在新项目中可直接去掉
过渡失效或卡顿?先查是否被 JS 频繁打断
很多“transition 不流畅”其实不是 CSS 写得不对,而是 JS 在同一帧内反复改样式,导致浏览器跳过中间状态、只取最终值。比如快速 hover 进出、滚动中批量切换 class、React/Vue 的状态更新未对齐渲染周期。
解决路径:
- 用 class 切换代替内联 style 修改(
el.classList.add('active')比el.style.opacity = 0更可靠) - 需要 JS 触发多次动画时,加
void el.offsetWidth强制同步读取当前样式,再设目标值 - 高频交互场景(如菜单悬停)加简单防抖:
setTimeout(() => el.classList.toggle('hover'), 50) - 禁用动画过程中的事件:
.el.animating { pointer-events: none; },防止 hover 堆积
真正难的不是怎么写 transition,而是判断“这里到底需不需要动”。很多卡顿,根源是过渡被滥用于本不该有动画的地方——比如表单输入框边框颜色变化、页脚年份更新。动效的价值不在填满变化,而在引导注意力。


















