如何消除汉堡菜单点击按钮与动画执行间的延迟?
汉堡菜单隐藏动画延迟问题解决方法
你的问题核心在于硬编码setTimeout时长和直接切换display: none的时机不当,导致隐藏动画看起来有延迟。下面给出两种优化方案,彻底解决这个问题:
方案一:用CSS过渡代替动画(推荐)
过渡比@keyframes更简洁可控,不需要写反向动画逻辑:
修改后的CSS:
.header__menu{ position: absolute; left: 0; top: 2.6em; flex-direction: column; width: 100vw; color: #fff; text-transform: lowercase; @include concert-one; font-size: 1.8rem; background-color: $violet; text-align: center; overflow: hidden; display: none; max-height: 0; /* 用过渡实现展开/收起动画 */ transition: max-height 1s ease-in; } /* 激活状态:显示菜单并展开 */ .header__menu.active { display: flex; max-height: 100vh; }
修改后的JS:
// 初始状态 hamburgerMenu.classList.remove('active'); hamburgerMenu.style.display = 'none'; hamburgerButton.addEventListener('click', () => { const isHidden = hamburgerMenu.style.display === 'none'; if (isHidden) { hamburgerMenu.style.display = 'flex'; // 强制浏览器重绘,确保过渡触发 void hamburgerMenu.offsetWidth; hamburgerMenu.classList.add('active'); } else { hamburgerMenu.classList.remove('active'); // 监听过渡结束后再隐藏 hamburgerMenu.addEventListener('transitionend', function handler() { hamburgerMenu.style.display = 'none'; hamburgerMenu.removeEventListener('transitionend', handler); }, { once: true }); } });
方案二:保留动画,监听动画结束事件
如果坚持使用@keyframes,不要用setTimeout,而是监听animationend事件精准触发隐藏:
修改后的CSS:
/* 合并展开/收起动画为一个,用reverse反向播放 */ @keyframes menuToggle { from { max-height: 0; } to { max-height: 100vh; } } .header__menu{ position: absolute; left: 0; top: 2.6em; flex-direction: column; width: 100vw; color: #fff; text-transform: lowercase; @include concert-one; font-size: 1.8rem; background-color: $violet; text-align: center; overflow: hidden; display: none; } .header__menu.appear { display: flex; animation: menuToggle 1s ease-in forwards; } .header__menu.hide { animation: menuToggle 1s ease-in reverse forwards; }
修改后的JS:
// 初始状态 hamburgerMenu.classList.remove('appear', 'hide'); hamburgerMenu.style.display = 'none'; hamburgerButton.addEventListener('click', () => { const isHidden = hamburgerMenu.style.display === 'none'; if (isHidden) { hamburgerMenu.classList.remove('hide'); hamburgerMenu.style.display = 'flex'; void hamburgerMenu.offsetWidth; hamburgerMenu.classList.add('appear'); } else { hamburgerMenu.classList.remove('appear'); hamburgerMenu.classList.add('hide'); // 动画结束后隐藏,避免硬编码时长 hamburgerMenu.addEventListener('animationend', function handler() { hamburgerMenu.style.display = 'none'; hamburgerMenu.removeEventListener('animationend', handler); }, { once: true }); } });
关键优化点说明
- 摒弃
setTimeout:浏览器的setTimeout受主线程任务影响,计时并不精准,监听transitionend/animationend事件才能准确捕捉动画结束时机。 - 强制重绘:
void hamburgerMenu.offsetWidth会触发浏览器重排,确保状态变化被识别,避免动画不触发的问题。 - 用类切换代替直接操作style:更符合CSS与JS分离的原则,代码更易维护。
内容的提问来源于stack exchange,提问作者cryon234
相关产品推荐
相关产品推荐

