最稳方案是position: fixed + inset: 0 + display: flex;避免top: 50%+transform因缩放、横屏、iOS地址栏跳变导致偏移;inset: 0更简洁可靠;遮罩须挂body顶层防层叠上下文干扰。
直接用 position: fixed + inset: 0 + display: flex 是最稳的响应式加载遮罩方案,100vw/100vh 在 iOS Safari 地址栏收放时会跳变,top: 50%; transform: translateY(-50%) 在页面缩放或 zoom 下会偏移,都该避开。
这个写法在桌面浏览器看着没问题,但移动端一缩放、一横屏、甚至双指 pinch-zoom,transform: translateY(-50%) 就会失准——它依赖元素自身高度计算,而字体渲染、行高、缩放比例都会让实际高度浮动。更麻烦的是,iOS Safari 的 vh 在地址栏隐藏/展开瞬间从 667px 跳到 736px,导致遮罩突然上移或下移一截。
实操建议:
display: flex; align-items: center; justify-content: center 居中,完全脱离尺寸依赖% 或 em 的垂直定位值,统一用 px 或 vh(仅用于字体大小,不用于布局)inset: 0 是 top: 0; right: 0; bottom: 0; left: 0 的简写,语义清晰、写法简洁,而且在所有支持 inset 的浏览器里行为一致。更重要的是,它能绕过某些旧版 Android WebView 对四边分别设值时的解析 bug。
立即学习“前端免费学习笔记(深入)”;
实操建议:
inset: 0,兼容性足够(Chrome 89+、Firefox 66+、Safari 14.1+)top: 0; left: 0; width: 100%; height: 100%,但必须加 overflow: hidden 防滚动条白边transform 的容器里,否则 inset: 0 会相对该容器定位,不是视口加了 pointer-events: none 却点不透,十有八九是遮罩里的文字、图标、SVG 默认继承了 auto,挡住了穿透路径。遮罩层本身设成 none 只是“不拦截”,不代表“自动透传”——它的子树仍按默认规则处理事件。
实操建议:
pointer-events: none
pointer-events: auto
onclick、addEventListener,否则事件流会被干扰opacity: 0 过渡,再 display: none 或 remove(),否则 pointer-events: none 会失效一帧opacity: 0.6 会让整个遮罩 DOM 树(包括里面的文字、SVG、图标)一起变灰、发虚,iOS Safari 渲染文字边缘会出现半透明锯齿,安卓部分 WebView 里点击区域也会弱化。真正只需要背景“透一点”,文字保持锐利可读。
实操建议:
background-color: rgba(0, 0, 0, 0.6)
background-color: #000(IE8- 用 filter: alpha(opacity=60))transition: background-color 0.25s,不是 transition: opacity
backdrop-filter: blur() 做动画,Safari 渲染卡顿明显,尤其低端 iPhone最易被忽略的一点:遮罩层必须挂到 <body> 最外层,不能嵌在某个 React/Vue 组件的局部 DOM 里。否则只要父组件触发了新的层叠上下文(比如带 z-index 的 position: relative),遮罩的 z-index 就只在那个小圈子里生效,再高也压不住 header 或导航栏。