移动端滑动菜单卡顿主因是误用left/top/height过渡,应仅对transform和opacity使用transition以确保60fps;错误写法触发重排重绘,正确做法是默认状态设transition及初始值,激活态只改值,并慎用will-change。

移动端滑动菜单卡顿,90%是因为用了 left、top 或 height 做过渡,必须只对 transform 和 opacity 写 transition,否则永远跑不满 60fps。
为什么 transform translateY() 滑动菜单在手机上一卡一顿
根本不是 JS 性能问题,而是浏览器被逼着每帧都做 layout(重排)和 paint(重绘)。比如用 top: -100px → top: 0,浏览器得反复计算文档流位置;而 transform: translateY(-100px) → transform: translateY(0) 只触发合成层更新,走 GPU,不碰布局树。
- 常见错误现象:
transition: all 0.3s或transition: top 0.3s,动画直接降级为软件渲染 - 父容器带
overflow: hidden时,transform动画内容可能被裁掉——检查最近的祖先是否意外加了隐藏 - 如果菜单用
position: absolute,又套在overflow: clip的导航栏里,滑入区域会直接消失
transition 必须写在默认状态,而不是 .open 类里
很多人把 transition: transform 0.3s ease 写在 .menu-open .nav-menu 里,结果点开瞬间就到位,没动画。因为 transition 要作用在“有初始值”的元素上,浏览器才能插值。
- 正确位置:
.nav-menu { transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.3s; } - 初始状态必须明确:比如
.nav-menu { transform: translateY(-100%); opacity: 0; } - 激活状态只改值,不改 transition:
.menu-open .nav-menu { transform: translateY(0); opacity: 1; } - 绝不要用
display: none控制显隐——它会中断整个 transition 链
max-height + transform 组合比单用 height 更稳
纯 transform 滑动适合固定高度菜单;但含动态内容(如文字行数不定、子菜单展开)时,光靠位移不够,得补高度变化感知。这时用 max-height 替代 height 是唯一轻量解法。
立即学习“前端免费学习笔记(深入)”;
- 初始:
max-height: 0; overflow: hidden; opacity: 0; visibility: hidden; - 展开前 JS 动态设:
element.style.maxHeight = element.scrollHeight + 'px'; - CSS 只过渡这两个属性:
transition: max-height 0.3s ease, opacity 0.3s; - 收起后记得在
transitionend事件里清空内联max-height,避免下次展开读错值
will-change 不是开关,是手术刀
will-change: transform, opacity 确实能提前建合成图层,但它会吃内存、增图层管理开销。移动端尤其敏感——别在页面加载时就全局加,更别给所有菜单常驻这个声明。
- 最佳时机:只在用户交互前一刻加,比如
touchstart或focus时el.style.willChange = 'transform, opacity'; - 动画结束后立刻清除:
el.addEventListener('transitionend', () => el.style.willChange = 'auto'); - 如果菜单里有文字,
transform: scaleY()可能导致模糊,换成transform: translateY()+opacity更稳妥
真正难的不是写出动效,而是判断该用哪组属性组合——transform 负责位移缩放旋转,max-height 负责高度伸缩,opacity 负责渐变,三者各司其职。混用 margin 或 padding 过渡,哪怕只加一行,也会让整段动画掉回 CPU 渲染。


















