仅用 opacity 配 transition 实现全屏遮罩不可靠,需同时处理渲染流参与(用 fixed 定位+vw/vh)、事件穿透(pointer-events 分层控制)和过渡时机(visibility 配 steps + opacity 监听过滤)。

直接用 opacity 配 transition 实现淡入淡出是可行的,但仅靠它做全屏遮罩层,大概率会遇到点不动、盖不住、闪一下、或动画不触发等问题——根本原因是没处理好渲染流参与、事件穿透和过渡时机这三件事。
为什么只改 opacity 不够用
单纯设 opacity: 0 并不能让元素“退出交互”,它仍占据空间、响应点击、挡住背后内容。用户可能误点遮罩层本身(比如想点后面按钮),或者遮罩没盖住 fixed 定位的导航栏。更隐蔽的问题是:如果遮罩父容器有 overflow: hidden 或缩放,100% 尺寸会失效,导致边缘漏白。
-
opacity只控制透明度,不控制是否参与事件分发 - 遮罩层必须脱离文档流,否则会被 flex/grid 布局挤压变形
- 移动端 touchstart 时若遮罩还没完全 visible,可能穿透到下层
pointer-events 必须显式控制
遮罩层默认要锁住底层交互,但内部关闭按钮又得能点——不能靠 JS 动态开关 pointer-events,得用 CSS 分层控制。
- 遮罩自身设
pointer-events: none,彻底透传点击(避免误锁) - 所有可操作子元素(如
.close-btn)强制设pointer-events: auto - 千万别在 JS 里用
el.style.pointerEvents = 'none'控制显隐,CSS 类切换更稳 - 如果遮罩内含表单或输入框,
pointer-events: none会让它们失焦,此时应改用visibility: hidden配合opacity
transition 的写法和监听要点
transition 必须写在初始状态(即隐藏时的 class 上),且只监听 opacity 变化,否则 transitionend 会触发两次(visibility 和 opacity 各一次)。
- 推荐写法:
transition: opacity 0.3s ease-out, visibility 0.1s steps(1, end)——visibility用steps(1, end)实现“瞬间切换”,避免残留不可见却可点的幽灵层 - JS 监听时务必加过滤:
if (e.propertyName === 'opacity'),再执行隐藏后逻辑(如remove()或重置状态) - 移除元素前,确保 transition 已结束;不要在加
.show类后立刻remove(),DOM 消失会导致transitionend永远不触发 - 避免用
transition: all,尤其当遮罩内有 transform 动画时,容易意外拖慢整个过渡
全屏尺寸与层级必须手写死
别信 width: 100%; height: 100% 能铺满全屏——body 有 padding、页面 zoom、iOS Safari 地址栏收缩都会让它错位。
- 固定用
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; -
z-index至少设9999,真实项目里被广告 SDK、第三方弹窗、sticky 导航栏压过太多次 - 如果遮罩要盖住
iframe(比如地图、视频),需额外加transform: translateZ(0)强制硬件加速,否则部分浏览器下 z-index 失效 - 不要依赖
:hover触发,移动端无效;用click+ class 切换,或监听touchstart(注意防重复)
最常被忽略的是:遮罩层的 visibility 切换时机和 transitionend 的属性过滤。这两个点一漏,要么动画卡半截,要么回调执行两遍,状态就乱了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











