opacity: 0 元素仍占布局且响应点击,需 visibility + opacity 联动过渡;clip-path 替代 transform 实现无损缩放;transition 必写在默认状态,监听 opacity 的 transitionend 后延迟销毁节点。

为什么只用 opacity 会“点不动”或“盖不住”
因为 opacity: 0 不等于隐藏——元素还在文档流里,仍响应点击、仍占布局空间、仍可能被父容器的 overflow: hidden 截断。常见表现:点关闭按钮没反应、滚动后遮罩错位、iOS 地址栏收缩时漏白。
真正起作用的是组合:visibility 控制是否参与渲染和事件分发,opacity 控制透明度,两者都支持平滑过渡,语义清晰、行为可控。
visibility + opacity 联动过渡怎么写
初始状态必须写死两步:
/* 遮罩层基础样式 */
.mask {
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
width: 100vw; height: 100vh;
z-index: 9999;
background: rgba(0,0,0,0.7);
visibility: hidden;
opacity: 0;
transition: visibility 0.1s steps(1, end), opacity 0.3s ease-out;
}
显示时加类(如 .mask--show):
.mask--show {
visibility: visible;
opacity: 1;
}
-
steps(1, end)确保visibility在过渡结束瞬间切换,避免中间态不可交互 - 不要把
transition写在.mask--show里,必须写在默认状态上 - JS 监听
transitionend时务必过滤:if (e.propertyName === 'opacity'),否则visibility变化也会触发一次
缩放动画要用 clip-path,不是 transform
想实现“从中心放大盖满全屏”的效果,transform: scale() 会拉伸内容、破坏文字清晰度;clip-path: circle() 才是语义正确的裁切方案,且 Safari 下必须加前缀和硬件加速。
起始状态写死圆心和半径:
.mask {
clip-path: circle(0 at 50% 50%);
-webkit-clip-path: circle(0 at 50% 50%);
transition: clip-path 0.6s cubic-bezier(0.22, 0.61, 0.36, 1),
-webkit-clip-path 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.mask--show {
clip-path: circle(150% at 50% 50%);
-webkit-clip-path: circle(150% at 50% 50%);
}
- Safari 默认圆心在左上角,不显式写
at 50% 50%就会偏移 - 结束半径用
150%而非100%,确保高 DPR 屏幕也完全覆盖 - 父容器加
transform: translateZ(0)或will-change: clip-path触发硬件加速,否则 Safari 动画易跳帧
别在 visibility: hidden 后立刻 remove()
visibility: hidden 是过渡终点,但 DOM 还在。如果 JS 在 transitionend 里直接 el.remove(),浏览器来不及完成过渡就销毁节点,transitionend 事件根本不会触发——导致逻辑中断、遮罩残留、后续无法再 show。
正确做法是:
- 监听
opacity的transitionend - 确认过渡完成后,再用
setTimeout(() => el.remove(), 0)延迟销毁(或改用display: none切换) - 更稳妥的是复用遮罩节点,只 toggle 类名,避免频繁创建/销毁
缩放 + 淡入要同时生效,clip-path 和 opacity 必须同级过渡,且 Safari 对前缀和硬件加速极其敏感——漏掉任意一项,动画就大概率闪动或卡住。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











