
本文详解为何 .mobile-nav.close 动画不触发,并提供基于 CSS animation + transform 的可靠实现,通过精准控制动画作用对象(ul 而非 nav)与初始状态,确保展开与收起均平滑执行。
本文详解为何 `.mobile-nav.close` 动画不触发,并提供基于 css `animation` + `transform` 的可靠实现,通过精准控制动画作用对象(`ul` 而非 `nav`)与初始状态,确保展开与收起均平滑执行。
在移动端导航动画开发中,一个常见却棘手的问题是:展开(fade in / slide down)动画正常,但收起(fade out / slide up)动画完全不生效——元素瞬间消失,毫无过渡效果。根本原因并非 JavaScript 逻辑错误,而在于 CSS 动画的触发机制与 DOM 渲染时序的配合失当。
问题核心在于:原始代码将 animation 直接绑定在 .mobile-nav.open 和 .mobile-nav.close 上,但 .mobile-nav 元素本身始终存在且 display: block,其 transform 初始值为默认 none。当添加 .close 类时,浏览器尝试从“无动画状态”直接跳转到 slideUp 动画,而由于 .mobile-nav 在未加类时并无预设 transform 基态,动画 from 关键帧无法被正确采样,导致动画被跳过。
✅ 正确解法是:将动画施加在子容器(如 <ul></ul>)上,并为其设置明确的初始 transform 状态,使 from 和 to 均有可计算的起始/结束值。
以下是经过验证的完整实现方案:
立即学习“前端免费学习笔记(深入)”;
✅ HTML 结构(精简优化)
<header class="mobile">
<div class="logo">
<img src="/media/yogism_logo_header.svg" alt="Logo" />
</div>
<nav class="mobile-nav">
<div class="hamburger-menu" id="hamburger-menu"></div>
<ul>
<li><a href="#">Features</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Testimonial</a></li>
<li><a href="#">Pricing</a></li>
<li><a href="#">Blog</a></li>
</ul>
</nav>
</header>? 注意:
<ul></ul>移至<nav></nav>内部并作为动画主体;汉堡图标改用 CSSbackground-image(支持 SVG Base64),避免 JS 频繁操作src属性引发重绘干扰。
✅ JavaScript 控制逻辑(简洁健壮)
document.addEventListener("DOMContentLoaded", function () {
const hamburgerMenu = document.getElementById("hamburger-menu");
const mobileNav = document.querySelector(".mobile-nav");
hamburgerMenu.addEventListener("click", function () {
mobileNav.classList.toggle("open");
// 确保 close 类仅在未 open 时存在(避免类冲突)
if (mobileNav.classList.contains("open")) {
mobileNav.classList.remove("close");
} else {
mobileNav.classList.add("close");
}
});
});✅ CSS 动画样式(关键修复点)
.mobile-nav {
display: block;
width: 100%;
background-color: var(--neutral-1000);
z-index: 1000;
position: absolute;
top: 0;
left: 0;
}
/* ? 核心:为 ul 设置初始隐藏状态(非 display: none!) */
.mobile-nav ul {
transform: translateY(-200%); /* 完全移出视口上方 */
margin: 0;
padding: 0;
list-style: none;
}
/* 展开:ul 从 -200% → 0 */
.mobile-nav.open ul {
animation: slideDown 0.3s ease forwards;
}
/* 收起:ul 从 0 → -200% */
.mobile-nav.close ul {
animation: slideUp 0.3s ease forwards;
}
@keyframes slideDown {
from { transform: translateY(-200%); }
to { transform: translateY(0); }
}
@keyframes slideUp {
from { transform: translateY(0); }
to { transform: translateY(-200%); }
}
/* 汉堡图标 SVG 切换(Base64 编码,零请求) */
#hamburger-menu {
width: 48px;
height: 48px;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
}
.open #hamburger-menu {
/* 替换为「X」图标 Base64(已压缩) */
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNIDEwIDEwIEwgOTAgOTAgTSAxMCA5MCBMIDkwIDEwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIwIiBmaWxsPSJub25lIiAvPgo8L3N2Zz4=');
}
/* 默认为汉堡图标 */
#hamburger-menu {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNIDUgMTUgaCA5MCBNIDUgNTAgaCA5MCBNIDUgODUgaCA5MCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyMCIgZmlsbD0ibm9uZSIgLz4KPC9zdmc+');
}⚠️ 关键注意事项
- ❌ 避免对
display: none/block切换做动画 ——display是离散属性,无法过渡; - ✅ 始终用
transform+opacity实现视觉进出动画,性能高且兼容好; - ✅
animation必须搭配forwards,否则动画结束后元素会回退到初始状态; - ✅
transform: translateY(-200%)中的-200%建议大于导航实际高度(如ul高 300px,则-300px或-200%更安全),确保完全不可见; - ✅ 若需支持 Safari,可补充
-webkit-animation前缀(现代项目通常无需); - ✅ 图标切换推荐使用 CSS
background-image而非 JS 修改src,避免图片加载抖动与跨域限制。
通过以上结构化调整,.mobile-nav.close ul 将严格触发 slideUp 动画,实现与展开完全对称、流畅、可预测的交互动效——不再“一闪而没”,而是优雅收起。


















