技术求助:如何实现滑动菜单的滑入滑出双向动画效果?
嘿,我瞅了下你的代码,问题出在这几个地方:
- 你直接用
display: none来隐藏菜单,这会让浏览器立刻把元素从布局中移除,动画根本来不及执行就没了; - JS里
hideMenu函数的判断写了if(navMenu.style.display = "block"),这是赋值操作不是判断,应该用==或者更靠谱的类名判断; - 还有个小bug:
#container里的display: absolute是错的,应该是position: absolute,不然布局会乱。
下面给你两种修复方案,推荐第一种用transition的,更简洁好维护:
方案一:用CSS Transition实现平滑切换(推荐)
这种方式通过控制元素的transform属性来实现滑入滑出,不用操心动画结束后的状态,代码更清爽。
修改后的CSS
#main-container { width: 350px; height: 600px; border: 1px solid black; position: absolute; overflow: hidden; } #container { width: 350px; height: 100%; position: absolute; /* 修复display:absolute的错误 */ z-index: 1; background: rgba(0, 102, 199, 0.8); } #navBtn { width: 50px; height: 50px; background: red; position: absolute; z-index: 999; } #navMenu { width: 250px; height: 100%; background: blue; position: absolute; z-index: 9999; top: 0; transform: translateX(-250px); /* 初始完全隐藏在左侧 */ transition: transform 0.5s ease; /* 0.5秒平滑过渡 */ } #navMenu.open { transform: translateX(0); /* 滑入到可视区域 */ } #navMenu.close { transform: translateX(-250px); /* 滑出到隐藏区域 */ }
修改后的JS
var navBtn = document.getElementById("navBtn"); var navMenu = document.getElementById("navMenu"); var container = document.getElementById("container"); // 初始状态移除所有控制类 navMenu.classList.remove('open', 'close'); navBtn.addEventListener("click", toggleMenu); container.addEventListener("click", hideMenu); function toggleMenu(e) { e.stopPropagation(); // 阻止点击按钮时事件冒泡到container,避免刚打开就关闭 if (navMenu.classList.contains('open')) { hideMenu(); } else { showMenu(); } } function showMenu() { navMenu.classList.add('open'); navMenu.classList.remove('close'); container.style.background = "rgba(0, 102, 199, 1)"; } function hideMenu() { navMenu.classList.add('close'); navMenu.classList.remove('open'); container.style.background = "lightblue"; // 如果需要动画结束后彻底隐藏(可选,其实transform已经看不见了) // navMenu.addEventListener('transitionend', function endTransition() { // navMenu.style.display = 'none'; // navMenu.removeEventListener('transitionend', endTransition); // }); }
方案二:保留原有的CSS Animation
如果你坚持想用原来的动画方案,那需要等滑出动画执行完再设置display: none,还要让动画结束后保持最终状态:
修改后的CSS
#main-container { width: 350px; height: 600px; border: 1px solid black; position: absolute; overflow: hidden; } #container { width: 350px; height: 100%; position: absolute; /* 修复display:absolute的错误 */ z-index: 1; background: rgba(0, 102, 199, 0.8); } #navBtn { width: 50px; height: 50px; background: red; position: absolute; z-index: 999; } #navMenu { width: 250px; height: 100%; background: blue; position: absolute; z-index: 9999; top: 0; display: none; } #navMenu.open { display: block; animation-name: slidein; animation-duration: .5s; animation-fill-mode: forwards; /* 保持动画结束后的状态 */ } #navMenu.close { animation-name: slideout; animation-duration: .5s; animation-fill-mode: forwards; } @keyframes slidein { 0% {transform: translateX(-250px);} 100% {transform: translateX(0px);} } @keyframes slideout { 0% {transform: translateX(0px);} 100% {transform: translateX(-250px);} }
修改后的JS
var navBtn = document.getElementById("navBtn"); var navMenu = document.getElementById("navMenu"); var container = document.getElementById("container"); navMenu.style.display = "none"; navBtn.addEventListener("click", toggleMenu); container.addEventListener("click", hideMenu); function toggleMenu(e) { e.stopPropagation(); // 阻止事件冒泡 if (navMenu.classList.contains('open')) { hideMenu(); } else { showMenu(); } } function showMenu() { navMenu.classList.remove('close'); navMenu.style.display = "block"; // 强制触发浏览器重绘,避免动画不生效 void navMenu.offsetWidth; navMenu.classList.add('open'); container.style.background = "rgba(0, 102, 199, 1)"; } function hideMenu() { navMenu.classList.remove('open'); navMenu.classList.add('close'); container.style.background = "lightblue"; // 动画结束后再隐藏菜单 navMenu.addEventListener('animationend', function endAnimation() { navMenu.style.display = 'none'; navMenu.classList.remove('close'); navMenu.removeEventListener('animationend', endAnimation); }); }
两种方案都能实现你要的反向滑出动画,选哪种看你习惯~
内容的提问来源于stack exchange,提问作者Tane




