Skip to content

弹层内 scroll-view 真机无法滚动

现象

  • 开发者工具中列表可以滚动。
  • 真机中列表能显示,但手指滑动无响应或内容无法完整查看。
  • 问题只出现在遮罩、抽屉或底部弹层内部。

原因

catchtouchmove 会拦截 touchmove 事件,常用于阻止弹层打开后背景页面继续滚动。如果绑定该事件的节点同时是 scroll-view 的祖先,内部滚动区域的滑动手势也可能被拦截。

xml
<!-- catchtouchmove 包住整个内容区,内部 scroll-view 可能无法滚动 -->
<view class="mask" catch:touchmove="preventMove">
  <view class="sheet">
    <scroll-view class="sheet__list" scroll-y>
      <view wx:for="{{items}}" wx:key="id">{{item.name}}</view>
    </scroll-view>
  </view>
</view>

开发者工具与真机的触摸事件行为可能存在差异,不能只依据模拟器结果判断。

结构调整

遮罩层与内容层使用兄弟节点。仅在遮罩层绑定 catchtouchmove,不要让它成为可滚动区域的祖先。

xml
<view wx:if="{{visible}}" class="picker-root">
  <view
    class="picker-backdrop"
    catch:touchmove="preventMove"
    bindtap="closePicker"
  ></view>

  <view class="picker-sheet">
    <view class="picker-sheet__title">选择门店</view>
    <scroll-view class="picker-sheet__list" scroll-y>
      <view wx:for="{{items}}" wx:key="id" class="picker-sheet__item">
        {{item.name}}
      </view>
    </scroll-view>
  </view>
</view>
js
Page({
  closePicker() {
    this.setData({ visible: false })
  },

  preventMove() {},
})
css
.picker-root {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.picker-backdrop {
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 55%);
}

.picker-sheet {
  position: relative;
  z-index: 1;
  background: #fff;
}

.picker-sheet__list {
  height: 50vh;
}

排查

  1. scroll-view 向上检查祖先节点,确认是否绑定了 catchtouchmove
  2. 为纵向 scroll-view 设置可确定的 height,不要只依赖 max-height 或无法计算剩余空间的 flex: 1
  3. 固定区域内的长列表优先使用 scroll-view,不要直接套用浏览器端的 view + overflow-y: auto 方案。
  4. 检查是否有透明节点覆盖列表,并确认遮罩与内容的 z-index 和点击区域。
  5. 使用真机调试验证滚动、遮罩点击和背景穿透。

通用遮罩组件如果需要同时承载可滚动弹层,应固定采用“遮罩层与内容层分离”的结构,避免将 catchtouchmove 绑定到整个弹层容器。

参考:

基于 MIT 许可发布