评论

如何彻底解决小程序滚动穿透问题

解决微信小程序滚动穿透的终极方案。

背景

俗话说,产品设计有三宝:弹窗、浮层加引导。可见“弹窗”在产品设计中的重要性。对于前端开发者来说,实现一个基础的模态框(Modal)并不难,可一旦模态框内部出现可滚动内容,各种意想不到的问题就会接踵而至——其中最为典型的,就是“滚动穿透”。

什么是滚动穿透?

滚动穿透指的是:当我们在顶层弹窗中执行滑动操作时,实际滚动的是底层页面的内容。这种体验非常影响交互,也常让用户感到困惑。

解决方案分析

解决滚动穿透的思路通常有两种:改变顶层或改变底层。

1. 改变顶层(不推荐)

一种自然的想法是阻止顶层的事件继续传播。例如,给遮罩层(蒙层)绑定 catchtouchmove 事件。但这种方法在部分场景下并不生效,因此并不是一个可靠的通用方案。

2. 改变底层(推荐)

既然问题是底层内容跟随滚动,那么只要在弹窗打开时禁止底层页面滚动,问题就迎刃而解。

具体实现方案

🟡 不成熟的方案

  • 将底层页面的最外层 view 设置为 position: fixed,但这会导致页面滚动位置丢失,回到顶部。

  • 另一种方式是:在打开弹窗前记录当前滚动位置,关闭弹窗后使用 wx.pageScrollTo 滚动回原位置。这种方法实现较为繁琐,且体验不够流畅。

✅ 成熟的方案

方案一:使用 page-meta 组件

通过微信小程序提供的 page-meta 组件,我们可以直接控制页面根容器的样式,类似于在 H5 中设置 body { overflow: hidden; }。

我们可以动态设置 overflow 属性为 hidden 或 auto,来控制页面是否可滚动。

方案二:使用 wx.setPageStyle 方法

这是一个更灵活的 API,特别适合在组件内部(如封装好的弹窗组件)使用,无需额外编写 page-meta 结构。

wx.setPageStyle({
  style: {
    overflow: 'hidden' // 或 'auto'
  }
})

代码示例

这里提供一个可供测试的代码片段,帮助你快速上手:

👉 点击查看代码片段

拓展:支付宝小程序的情况

支付宝小程序虽然也支持 page-meta 组件,但由于其 WebView 内核限制(版本 69),设置 overflow: hidden 并不能完全阻止底层滚动。

支付宝团队为此专门提供了新的 API:my.setPageScrollable,用于精确控制页面是否可滚动。

my.setPageScrollable({
  scrollable: false,
  success: (res) => console.log(res),
  fail: (err) => console.log(err)
})

更新(2025.06.18)

该 API 已正式开放。不过目前测试发现:在 安卓端 设置禁止滚动后,弹窗内的可滚动区域也会被一并禁止;iOS 端 则表现正常,仅底层页面被锁定。

原因分析:

安卓与 iOS 在滚动禁止的层级控制上存在系统级差异:

  • 安卓 采用 Webview 级别的滚动限制,生效时整个页面(包括所有弹层)均不可滚动;
  • iOS 采用组件级控制,能智能区分层级,仅锁定底层页面而保持弹窗内部可滚动。
最后一次编辑于  11-13  
点赞 52
收藏
评论

22 个评论

  • 蟑螂恶霸
    蟑螂恶霸
    2021-05-12

    良心帖子,我现在就去试

    2021-05-12
    赞同
    回复
  • 大步向前
    大步向前
    2021-05-02

    很赞,算是看到一个用官方的解决方法的人了。

    我试试看能不能封装称组件。

    2021-05-02
    赞同
    回复

正在加载...

登录 后发表内容