iOS弹窗需精准还原动效、遮罩、手势与层级:入场用scale+translateY+ease-out,禁JS动画;遮罩用absolute+100vw/vh+mask-opacity;App端禁用popGesture并动态控制;遮罩挂载page根节点,H5锁body滚动。

uni-app 实现仿 iOS 弹窗,核心不是套个圆角白框就完事——iOS 用户对动效节奏、遮罩响应、手势中断、层级关系极其敏感。直接用 uni.showModal 或默认 uni-popup 会明显“不像”,尤其在 App 端。
为什么默认弹窗在 iOS 上显得“假”
常见现象:弹窗出现时没有淡入+微微上浮动画;点击遮罩不关闭;侧滑返回直接退出页面而非关闭弹窗;蒙层颜色发灰或滚动时错位。
- iOS 原生 modal 是模态阻断式,会暂停底层页面所有交互(包括 scroll-view 滚动),而 uni-app 默认弹窗仍允许背景穿透点击
-
uni.showModal是系统级 API,样式不可控,且 H5/小程序/App 三端表现不一致,无法做圆角、阴影、自定义按钮等 iOS 特征 - App 端 WebView 的
popGesture默认开启,用户左滑会直接退页,而不是关闭弹窗 - 遮罩层若用
position: fixed,在 iOS WebView 中极易随页面滚动“漂移”,必须改用absolute+100vw/100vh
自定义组件中实现 iOS 动效与交互
关键不是写多炫的 CSS 动画,而是匹配 iOS 的 timing 和行为逻辑。推荐用 transition 配合 transform 控制入场/退场,禁用 JS 动画库(避免卡顿)。
- 入场动画:用
transform: scale(0.92) translateY(20px)→scale(1) translateY(0),配合ease-out,时长 300ms - 遮罩层透明度从
mask-opacity: 0→0.4,同步过渡,避免突兀 - 弹窗内容区域加
@touchmove.prevent,防止内部滚动触发底层页面滚动 - 遮罩层必须单独监听
@click,不能依赖mask-click属性——该属性在 scroll-view 内或嵌套 view 中常失效 - 按钮文字大小统一用
17px(iOS 系统字体基准),取消所有text-transform: uppercase
App 端必须处理的手势冲突
这是最容易被忽略、但用户感知最强的一环。iOS 侧滑返回和弹窗关闭必须解耦,否则用户一滑就退页。
- 弹窗打开时,立即执行:
this.$mp.page.$getAppWebview().setStyle({ popGesture: 'none' }) - 弹窗关闭后,恢复:
this.$mp.page.$getAppWebview().setStyle({ popGesture: 'close' }) - 注意:该操作仅对 App 端生效,H5 和小程序无需处理;且必须在
onShow后调用,不能在mounted里(Webview 尚未就绪) - 如果弹窗内含输入框,聚焦时需额外监听
focusin事件并临时恢复popGesture,否则键盘弹起后无法侧滑返回
遮罩与层级的跨端陷阱
遮罩层在 iOS 上“发灰”、在安卓上“盖不住下拉刷新”、在 H5 上“点不透”,本质是 stacking context 断裂或挂载位置错误。
- 遮罩层 DOM 必须挂载到
<page></page>根节点下(App/H5),不能嵌套在scroll-view或任何带transform的容器里 - H5 端打开弹窗时,手动加
document.body.style.overflow = 'hidden';关闭时还原 - App 端若启用了深色模式(
"darkmode": "follow"),遮罩层需显式声明color-scheme: light,否则系统会自动降饱和度 - 遮罩背景色不要设
background-color,它不起作用;改用mask-opacity控制透明度,或用自定义<cover-view></cover-view>(仅 App 端)承载渐变/图片
最麻烦的从来不是写出来,而是让遮罩不飘、动效不卡、手势不抢、三端不裂——这些细节没压住,再像的 UI 也只是一张静态图。


















