必须手写组件,禁用position:fixed改用absolute+100vw/100vh,H5加translateZ(0)加速,App端遮罩挂page根节点,同时写@tap和@touchmove.stop.prevent阻断事件。
直接用 uni.showmodal 肯定不行——它样式锁死、没法加图标、不能嵌表单,连圆角都调不了。真要统一风格、多页面复用,必须手写组件,但别急着套 v-model 或 position: fixed,这两个是踩坑最密集的起点。
遮罩层为什么点不关闭、还跟着页面滚动?
根本原因是 position: fixed 在 iOS WebView 和部分安卓 WebView 里渲染异常,尤其遇到 scroll-view 或下拉刷新时,遮罩会“悬空”。H5 端点不关闭,大概率是事件没拦住;App 端遮罩飘走,说明它没挂对位置。
- 遮罩层改用
position: absolute; top: 0; left: 0; width: 100vw; height: 100vh;,别碰fixed - H5 端加
transform: translateZ(0)强制硬件加速,防闪烁 - App 端必须把遮罩节点挂到
page根节点下(不能嵌在scroll-view或页面view里),否则会被裁剪或层级压住 - 遮罩层必须同时写
@tap="close"和@touchmove.stop.prevent,只写catchtouchmove在 App/H5 上完全无效
弹窗内容区怎么防止点击穿透和滚动错乱?
点到遮罩后面按钮触发了、或者弹窗里一滑页面也跟着动——这不是 bug,是你没切断事件流。uni-app 的事件捕获能力比小程序弱,尤其在微信内置浏览器和低端安卓机上。
- 遮罩层本身设
pointer-events: auto(默认就是,但有人误设成none) - 弹窗主体容器加
@touchmove.stop.prevent,阻止内部滚动触发背景滚动 - 打开弹窗时,H5 端执行
document.body.style.overflow = 'hidden';App 端建议通过uni.$emit全局通知禁用滚动,关闭时再恢复 - 别依赖父容器的
overflow: hidden控制,scroll-view会创建新层叠上下文,导致z-index失效
动画卡顿、闪一下、关不掉残留 DOM 怎么办?
<transition></transition> 在 App 端(尤其 iOS)对 opacity 和 transform 支持不一致,首帧白屏、动画跳过、关闭后 DOM 还在都是高频问题。
- 放弃
<transition></transition>,改用uni.createAnimation手动控制,兼容性更好,且能精确绑定animationend回调 - 入场动画用
transform: scale(0.95); opacity: 0起始,别用height或margin(触发布局重排) - 动画时长不低于
200ms,iOS WebView 对300ms以下动画支持差 - 务必在
animationend回调里清理状态,比如this.show = false,否则 App 端可能内存泄漏
跨平台尺寸错乱、底部被手势条挡住怎么办?
px 在 App 端按物理像素渲染,rpx 才缩放;iPhone 全面屏底部安全区不处理,按钮就卡在手势条下面——上线后才被用户反馈,这种坑最伤。
- 所有尺寸(
border-width、padding、font-size)一律用rpx - 必须用 px 的地方(如图标字体),加平台判断:
/* #ifdef APP-PLUS */ border-width: 1px; /* #endif */ - App 端
z-index得设到9999起,否则原生导航栏会盖住弹窗 - 弹窗底部留白加
padding-bottom: env(safe-area-inset-bottom),并确保父容器没设overflow: hidden吞掉这个值
最麻烦的不是写不出来,而是 H5、App、小程序三端对同一个 CSS 属性反应不同——比如 transform: scale() 在 H5 可能让遮罩脱离层叠上下文,App 端又要求必须挂载到 page 根节点。动手前先想清楚:这个弹窗到底要在哪几个平台跑,哪些特性可以妥协,哪些必须保。










