必须用 transform 和 opacity 实现动画,禁用 height/width/left;因 height: auto 无法插值导致跳变,max-height 模拟需设合理上限;旧浏览器需显式声明 transition,用 backface-visibility 或 translate3d 启用 GPU 加速,Layer Borders 验证提层。

直接换用 transform 和 opacity,别碰 height、left、width —— 这不是优化建议,是硬性要求。其他方案要么治标不治本,要么在旧版浏览器里直接失效。
为什么 transition: height 0.3s 一定会卡
浏览器根本没法对 height: auto 插值:它不知道 “auto” 等于多少像素,于是放弃过渡,直接跳变。哪怕你用 JS 先读 scrollHeight 再设 max-height,也容易因同步 layout 触发首帧卡顿。
- 常见错误写法:
transition: height 0.3s ease+height: auto - 替代方案必须量化:用
max-height模拟,但上限值要贴近真实内容(比如最大可能 420px,就设max-height: 450px,别写9999px) - 初始状态必须是
max-height: 0+overflow: hidden,不能靠display: none切换
transition 在旧浏览器里失效或卡顿的真正原因
IE11、Android 4.4 WebView、Safari 9 这些环境根本不信任简写或继承来的 transition 声明。它们会直接忽略,退化成跳变。
- 必须显式写出完整声明:
transition: transform 0.3s cubic-bezier(.25,.46,.45,.94), opacity 0.3s ease -
will-change: transform在 IE11 完全无效,在 Android 4.4 可能闪屏,别用 - 更稳的 GPU 提示是
backface-visibility: hidden或transform: translate3d(0, 0, 0)(注意不是translateZ(0)) - 媒体查询里重写
transition,必须把所有要过渡的属性都列全,否则旧引擎清空前值
怎么确认 GPU 加速真生效了
写了 transform 不等于进了合成层。很多卡顿其实发生在“你以为加速了,其实没提层”的状态下。
立即学习“前端免费学习笔记(深入)”;
- Chrome DevTools → Rendering → 勾选 “Layer Borders”:只有带绿色边框的元素才算成功提升
- 如果没绿框,检查父容器是否设了
overflow: hidden或filter—— 这两个会抑制子元素提层 - 动画中别动态改
box-shadow或background-color,它们会让图层合并失败,整条transition回退到 CPU 渲染 - 移动端尤其注意:iOS Safari 9–10 的
transitionend可能丢失,得加setTimeout兜底清理临时 class 或will-change
JS 控制 transition 时最容易踩的坑
不是 JS 写错了,而是读写顺序错了。一次 offsetHeight 就能让整段动画卡住第一帧。
- 别在 click 回调里直接写:
el.style.maxHeight = el.scrollHeight + 'px' - 正确顺序:
el.style.maxHeight = '0'→ 强制触发重排(如读el.offsetHeight)→requestAnimationFrame→ 再设目标值 - iOS Safari 15 前,
scrollHeight在visibility: hidden下返回 0,得临时切回visible再测 - 更稳方案是用
ResizeObserver监听高度变化,而不是靠 JS 主动查
最常被忽略的一点:动画过程中偷偷读取布局(比如 getBoundingClientRect())、或者父容器有 filter 却没意识到它会拖垮子元素的合成性能——这些不会报错,但会让 60fps 彻底消失。


















