不能直接用 height + transition 实现平滑展开,因为 height: auto 不可动画;应使用 max-height 模拟过渡,并同步控制 opacity 和 visibility,配合 overflow: hidden 实现稳定零 JS 动画。

不能直接用 height + transition 实现平滑展开,因为 height: auto 不可动画,浏览器无法插值计算。 唯一稳定、零 JS 的方案是用 max-height 模拟高度过渡,并配合 overflow: hidden 和显隐控制。
为什么 height + transition 会跳变
常见错误是这样写:
.menu { height: 0; transition: height 0.3s; }
.menu.open { height: auto; }结果是“啪”一下弹出来——height: auto 不是数值类型,浏览器无法在 0px 和 auto 之间生成中间帧。即使设成具体像素(如 height: 200px),一旦内容动态变化(比如文字换行、异步加载),就极易截断或撑破。
max-height 的安全取值怎么定
关键不是“无限”,而是“足够大但不过量”:
立即学习“前端免费学习笔记(深入)”;
-
max-height: 1000px:适合内容高度相对固定的场景(如固定条目数的下拉菜单) -
max-height: 100vh:响应式友好,但 Safari 移动端中vh会随地址栏显示/隐藏波动,可能造成动画抖动 -
max-height: calc(100vh - 80px):更稳妥,预留顶部导航等固定区域空间 - 避免
max-height: none:它彻底禁用过渡,动画失效
必须同步控制的三个属性
只靠 max-height 过渡,收起时边缘仍可能“突兀消失”或点击穿透。要真正平滑,需三者联动:
-
max-height:负责高度过渡(从0到上限值) -
opacity:负责淡入淡出(从0到1),避免视觉闪跳 -
visibility:负责点击穿透控制(hidden→visible),它不参与transition,但必须和类切换同步生效
推荐写法:
.menu {
max-height: 0;
opacity: 0;
visibility: hidden;
overflow: hidden;
transition: max-height 0.3s ease, opacity 0.3s ease;
}
.menu.open {
max-height: 600px;
opacity: 1;
visibility: visible;
}容易被忽略的性能与兼容细节
看似简单,实操中几个点常被跳过:
- 别写
transition: all 0.3s:它会意外触发动画border、padding、color等,拖慢渲染且干扰预期效果 - 如果菜单含
padding或border,它们不会随max-height自动缩放;需要显式加入transition,例如:transition: max-height 0.3s, padding 0.3s, border 0.3s - JS 动态插入内容后,必须等 DOM 渲染完成再添加
.open类,否则scrollHeight读取不准,上限值可能偏小 - 移动端 iOS Safari 对
max-height过渡有轻微卡顿倾向,建议把缓动函数换成cubic-bezier(0.25, 0.8, 0.25, 1)替代ease,更顺滑
真正麻烦的从来不是“怎么动”,而是“动到哪停”和“动完还准不准”——上限值没估准,动画就断在半路;显隐没配齐,用户就点不到或看到空白闪烁。


















