切换CSS类时实现遮罩始终从上到下滑动的动画问题
解决遮罩每次点击都从上到下滑动的问题
我明白你的困扰——用CSS过渡的话,移除类时遮罩会反向滑动,动画又没法在移除类时触发想要的效果。其实核心问题是:过渡是基于属性的起始/结束值反向运行,而我们需要每次切换都强制执行从上到下的单向动画,不管是显示还是隐藏状态。
下面给你两种可行的方案,按需选择:
方案一:用CSS动画控制单向滑动
这种方式通过定义两个独立的动画,分别控制遮罩的显示(滑入)和隐藏(滑出),每次切换时触发对应的动画,确保方向都是从上到下。
HTML 结构
<button class="toggle-mask">切换遮罩</button> <div class="mask"></div>
CSS 样式
.mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); /* 默认隐藏在屏幕顶部 */ transform: translateY(-100%); pointer-events: none; /* 不干扰底层交互 */ } /* 显示动画:从上到下滑入屏幕 */ @keyframes slideDownIn { 0% { transform: translateY(-100%); } 100% { transform: translateY(0); } } /* 隐藏动画:从上到下滑出屏幕底部 */ @keyframes slideDownOut { 0% { transform: translateY(0); } 100% { transform: translateY(100%); } } /* 显示状态类 */ .mask.show { pointer-events: auto; animation: slideDownIn 0.5s forwards; } /* 隐藏状态类 */ .mask.hide { animation: slideDownOut 0.5s forwards; }
JavaScript 逻辑
const toggleBtn = document.querySelector('.toggle-mask'); const mask = document.querySelector('.mask'); let isMaskVisible = false; toggleBtn.addEventListener('click', () => { // 先移除所有动画类,避免冲突 mask.classList.remove('show', 'hide'); if (!isMaskVisible) { // 显示遮罩:触发滑入动画 mask.classList.add('show'); isMaskVisible = true; } else { // 隐藏遮罩:触发滑出动画 mask.classList.add('hide'); isMaskVisible = false; // 动画结束后重置遮罩位置,方便下次触发 mask.addEventListener('animationend', () => { mask.classList.remove('hide'); mask.style.transform = 'translateY(-100%)'; }, { once: true }); } });
方案二:用过渡+强制重绘实现单向滑动
如果你更倾向于用CSS过渡而非动画,可以通过强制浏览器重绘,让遮罩在隐藏时先回到顶部,再触发向下滑动的过渡。
CSS 样式
.mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); transform: translateY(-100%); transition: transform 0.5s ease; pointer-events: none; } .mask.active { transform: translateY(0); pointer-events: auto; }
JavaScript 逻辑
const toggleBtn = document.querySelector('.toggle-mask'); const mask = document.querySelector('.mask'); let isActive = false; toggleBtn.addEventListener('click', () => { if (!isActive) { // 显示遮罩:正常触发过渡 mask.classList.add('active'); isActive = true; } else { // 第一步:强制遮罩回到顶部 mask.style.transform = 'translateY(-100%)'; // 强制浏览器重绘,确保识别这个状态 void mask.offsetWidth; // 第二步:设置遮罩滑到屏幕底部,触发过渡 mask.style.transform = 'translateY(100%)'; // 过渡结束后重置状态 mask.addEventListener('transitionend', () => { mask.classList.remove('active'); mask.style.transform = 'translateY(-100%)'; isActive = false; }, { once: true }); } });
两种方案的核心都是打破过渡的反向逻辑,强制每次点击都执行从上到下的滑动动画。你可以根据自己的代码结构选择合适的方式~
内容的提问来源于stack exchange,提问作者Kuba
相关产品推荐
相关产品推荐

