最稳方案是position: fixed + inset: 0 + display: flex;top: 50%+transform因缩放、横屏、iOS地址栏跳变导致偏移,且依赖自身高度计算,易受字体渲染、DPR、WebView兼容性影响。

直接用 position: fixed + display: flex 是目前最稳的方案,其他写法在 iOS Safari 缩放、地址栏收放、高 DPR 屏幕或老版 WebView 下容易偏移、漏边或错位。
为什么不能只用 top: 50% + transform: translate(-50%, -50%)
这个组合依赖元素自身尺寸计算居中位置,而字体渲染、缩放比例、横屏切换、甚至双指 pinch-zoom 都会让实际高度浮动。iOS Safari 在地址栏隐藏/展开瞬间会重算 100vh,从 667px 跳到 736px,遮罩就会突然上移或下移一截。更麻烦的是,它在安卓某些 WebView 中对 transform 的解析也不一致。
-
top: 50%+left: 50%是相对父容器定位,不是视口 -
transform不触发合成层优化时,动画还可能卡顿 - 一旦页面有
zoom或font-size变动,偏移就不可控
必须写的 CSS 属性和值(缺一不可)
以下属性必须同时存在,否则要么遮罩不全屏,要么内容不居中,要么被其他组件盖住:
-
position: fixed—— 锚定视口,不随滚动偏移 -
inset: 0(或top: 0; right: 0; bottom: 0; left: 0)—— 比width: 100vw; height: 100vh更可靠,绕过旧版 WebView 解析 bug -
display: flex+justify-content: center+align-items: center—— 单靠其中任一只能单向居中 -
z-index: 9999—— 查过当前页面最高z-index(如 Ant Design 是 1000,Element Plus 是 2000),取明显更大值 -
background-color: rgba(0, 0, 0, 0.6)—— 别用opacity,否则加载图标也会变淡
移动端键盘弹出、页面缩放时遮罩错位怎么办
这不是你写错了,是浏览器对视口单位的解释差异导致的。iOS Safari 软键盘弹出时,100vh 会按“可用视口”重算,可能变小;Chrome/Safari 新版支持 100dvh(dynamic viewport height),但得 fallback。
- 优先用
min-height: 100dvh,再 fallback 到min-height: 100vh - 给
body加overscroll-behavior: contain,防止遮罩激活时还能滚动底层内容 - 遮罩容器别设
overflow: hidden,某些安卓 WebView 下会裁剪动画 - 如果遮罩内含输入框,需监听
focusin临时提升z-index,否则软键盘可能把它顶出视图
pointer-events 必须分层控制
遮罩层本身要拦截点击,但里面的按钮、链接、SVG 图标又得能点——这靠的是分层设置 pointer-events,不是一刀切。
- 遮罩根元素设
pointer-events: none - 所有可交互子元素(如关闭按钮、重试链接)显式写
pointer-events: auto - 移除遮罩时,先
opacity: 0过渡,再display: none或remove(),否则pointer-events: none会失效一帧 - 别给遮罩层绑
onclick或addEventListener,事件流会被干扰
最容易被忽略的是:遮罩层必须保持 pointer-events: auto(默认值),禁用交互靠的是给业务逻辑加限制,而不是靠样式“关掉”整个树。一旦忘了给子元素恢复 pointer-events: auto,那个「取消」按钮就永远点不了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











