最推荐 opacity + visibility 组合实现淡入淡出,需同时设置 opacity 控制透明度、visibility 控制交互与占位,并搭配 transition 实现“先显隐后渐变”;慎用 max-height 和纯 display 切换。

display: none 和 display: block 无法触发 CSS 过渡动画,这是最常踩的坑。必须用可动画的属性(如 opacity、transform、max-height)来控制显隐。
用 opacity + visibility 实现淡入淡出
这是最直观、兼容性最好的方案,适合大多数下拉菜单结构(比如 Bootstrap 的 .dropdown-menu)。
关键点在于:不能只靠 opacity,否则元素仍占位、可被聚焦或点击;必须搭配 visibility: hidden/visible 控制交互状态。
-
visibility: hidden使元素不可见且不可交互,但保留文档流位置 -
opacity: 0控制透明度,配合transition: opacity .2s ease, visibility 0s .2s实现“先隐藏再淡出”“先淡入再显示” - 初始状态设为
opacity: 0; visibility: hidden;,悬停/展开时改为opacity: 1; visibility: visible; - 注意:如果父容器有
overflow: hidden,opacity: 0不影响裁剪,但visibility: hidden会彻底移除渲染,更安全
用 transform: scaleY() + opacity 模拟展开感
比纯淡入更有“下拉”动效,视觉上更接近原生体验,但需确保父容器 position: relative 且子菜单 position: absolute。
立即学习“前端免费学习笔记(深入)”;
- 初始状态:
transform: scaleY(0); opacity: 0; transform-origin: top; - 展开状态:
transform: scaleY(1); opacity: 1; - 必须加
transform-origin: top,否则缩放中心在中间,看起来像“炸开”而非“向下展开” - 慎用
scaleY(.99)等非整数值——某些浏览器(尤其是旧版 Safari)对 subpixel 缩放渲染异常,导致边缘模糊或抖动 - 过渡写法推荐:
transition: transform .25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity .25s ease,用贝塞尔曲线让展开开头更轻快
避免用 max-height 做动画的三个现实问题
网上很多教程推荐 max-height: 0 → max-height: 300px,但它在真实项目中容易翻车:
- 必须预估最大高度(比如 300px),一旦子项动态增减或字体缩放,就会出现截断或留白
-
max-height动画在 Firefox 中有已知渲染卡顿,尤其内容较多时帧率明显下降 - 无法与
opacity或transform同时流畅过渡——浏览器会降级为 layout 触发,性能差 - 若菜单含
padding、border或box-shadow,max-height动画无法带动这些属性渐变,视觉割裂
Bootstrap 5 下拉菜单如何注入自定义动画
Bootstrap 5 默认用 JS 控制 show 类切换,不带 CSS 动画。要加淡入展开,得覆盖默认行为:
- 移除
.dropdown-menu.show的display: block,改用opacity和transform - 在 CSS 中添加:
.dropdown-menu { transition: opacity .2s ease, transform .25s ease; }和.dropdown-menu.show { opacity: 1; transform: scaleY(1); } - 关键:Bootstrap 5 的
.dropdown-menu默认有display: none,你必须用!important覆盖它,或提高选择器权重(比如.dropdown.show .dropdown-menu) - 如果用了 Popper.js(Bootstrap 5 默认),还需禁用其内置的
flip和shift行为,否则动画过程中位置可能跳变
真正难的不是写几行 @keyframes,而是让动画在各种内容长度、设备缩放、焦点管理下都稳定不闪、不卡、不遮挡。优先选 opacity + visibility 组合,它最经得起压测。


















