We前端之UniApp禁止遮罩弹窗下的页面跟随滚动、彻底解决小程序滚动穿透问题、微信小程序、滑动、穿透
MENU
微信小程序
背景
俗话说,产品有三宝:弹窗、浮层加引导,足以见弹窗在产品中的地位。对任意一个刚入门的前端同学来说,实现一个模态框基本都可以达到信手拈来的地步,但是,当模态框里边的内容滚动起来以后,就会出现各种各样让人摸不着头脑的问题,其中,最出名的想必就是滚动穿透。
什么是滚动穿透?
滚动穿透的定义:指滑动顶层弹窗,但效果上却同时滑动了底层的内容。
解决方案具体分析
改变顶层
从穿透的思路考虑,如果顶层不会穿透过去,那么问题就解决了,尝试给蒙层加catchtouchmove,但发现部分场景无效果,且顶层自己也无法滑动(真机调试)。
改变底层
既然是顶层影响了底层,要是底层不会滚动,那就没这个问题了。
如何改变底层解决该问题呢?
不成熟方案
1、底部页面最外层view设置position: fixed;页面不可滚动,但是这个时候会导致页面回到顶部。
2、滚动时监听滚动距离,弹窗时记录滚动位置,关闭弹窗后使用wx.pageScrollTo回滚到记录的位置。
成熟方案
1、使用page-meta组件,通过该组件我们可以操作Page的style样式,类似于h5里body设置overflow: hidden;控制页面不可滚动。
2、使用wx.setPageStyle设置overflow: hidden, 也可以实现给Page组件设置样式。
page-meta组件
通过该组件就可以直接操作Page组件,给它的wxss样式overflow动态设置hiddenorvisibleorauto就可以控制整个页面是否可以滚动。
wx.setPageStyle方法
调用
wx.setPageStyle这个api,动态设置它为hidden/auto,用于控制页面是否可滚动,主要用于页面组件内,比如封装好的弹窗组件,就不用单独写page-meta组件了。wx.setPageStyle({ style: { overflow: 'hidden' // 'auto' } });
wx.setPageStyle方法详解
版本支持
wx.setPageStyle基础库最低支持版本3.3.0+,用来动态修改页面的样式。
设置
wx.setPageStyle({ style: { overflow: 'hidden' } });等价于把页面的body或根节点的overflow设置成hidden,效果就是「禁用滚动」。
恢复(自动)
wx.setPageStyle({ style: { overflow: 'auto' } });
恢复(移除样式)
wx.setPageStyle({ style: { overflow: '' } });
推荐用法
禁用滚动:overflow: 'hidden'
恢复滚动:overflow: 'auto'或overflow: ''
拓展
支付宝小程序虽然存在page-meta组件,但是由于内核为69版本,给page设置
overflow: hidden;也无法控制底部元素不可滚动,目前已联系支付宝的底层开发人员提供API控制页面disableScroll,目前正在封装Appx,近期开放。my.setPageScrollable({ scrollable: true, success: res => { console.log(res); }, fail: err => { console.log(err); }, complete: res => { console.log(res); } });2025年6月18日API已开放,支付宝小程序测试时发现bug,安卓设置禁止滚动后,弹窗内的可滚动区域也会被禁止,IOS正常,且该问题暂时无法解决。
原因:由于系统实现层面的差异,安卓与iOS对于滚动禁止的层级控制存在区别。
安卓端采用Webview级滚动限制(全页面锁定),生效时界面及所有弹层均不可滚动;
iOS端采用组件级滚动限制(局部锁定),当弹层激活时会智能区分层级,仅限制底层页面滚动而保持弹层可滚动。
uniApp
问题
最近使用uniApp开发一款软件,父页面是可上下滑动的长列表,自定义组件弹窗遮罩出来后,滑动屏幕,父页面也跟着滚动。
在遮罩层的最外层view元素中加入@touchmove.stop.prevent="moveHandle"或@catchtouchmove="moveHandle"。
亲测有效,但不理想,会导致弹窗页面也无法滚动,或者处理不当就报一些其他错误,还是不太简便。
解决
问题根源是浏览器的滚动穿透,目前还没有根本性办法解决,但可以在弹窗遮罩出来时给最外层容器设置高度来解决,亲测这个办法还是比较完美。
弹出遮罩时动态设置页面最外层容器style属性的height值为100vh。 这里最好使用vh单位,100vh表示整个屏幕的高度,因为页面没有超出一屏,所以在弹窗上滑动时便不会触发底层的滚动事件,自然不会再有滚动穿透的问题。
此做法需要自定义列表滚动容器,也不是个很好的办法,表示无解
代码
<template>
<view class="container" :style="showMsk ? 'height: 100vh' : ''">
<!-- 滚动列表 -->
<scroll-view scroll-y="true"></scroll-view>
<!-- 自定义弹窗 -->
<view v-if="showMsk" class="msk"></view>
<!-- 自定义弹窗 -->
<!-- <view v-if="showMsk" class="msk" @touchmove.stop.prevent="moveHandle"></view> -->
</view>
<template>
data() {
return {
showMsk: false,
};
},
methods: {
moveHandle: {
return false;
}
}
更多推荐


所有评论(0)