微信小程序开发scroll-view
Published on Mar 08, 2025, with 34 view(s) and 0 comment(s)
Ai 摘要:本文总结了微信小程序开发中scroll-view组件的两个常见问题及解决方案:1) 启用动画滚动时tab跳转位置与实际显示不一致,建议禁用动画或添加节流控制;2) iOS端惯性滚动时设置scroll-into-view导致白屏,推荐检测滚动状态避免冲突。核心解决思路是避免滚动过程中操作tab,并提供了检测滚动停止的代码示例和元素相交判断方法(IntersectionObserver API)。最终方案强调体验优先,建议禁用动画滚动并严格分离滚动与tab切换操作。

scroll-view tab跳转指定位置,active tab和实际的位置不一致


描述: scroll-view 在scroll-with-animation="true"情况下,使用tab切换scroll-into-view导致active tab和实际的位置不一致  解决方案:

  1. 添加tab节流处理,一段时间内点击一次。

  2. 不使用scroll-with-animation="true"

  3. 滚动时不能点击tab


记录:使用scroll-with-animation="true"点击tab导致头部渐变效果很不协调

scroll-view白屏


描述:scroll view ios端在惯性滚动期间突然设置scroll-into-view导致内容白屏,点击后恢复  解决方案:

  1. 牺牲体验:使用scroll-with-animation="true",使之有滚动过程。

  2. 京东购物详情:滚动时不能点击tab

  3. nike:上两种方案兼用


方案确定

无论是上述哪种情况,滚动时切换tab都会出现问题。 不在滚动的时候切换tab。 切换tab时不让其滚动。

体验至上,不使用scroll-with-animation;

附一:如何检测页面停止滚动

Page({
  data: {
    isScrolling: false, // 是否正在滚动
  },

  onScroll(e) {
    // 设置滚动状态为 true
    this.setData({ isScrolling: true });

    // 清除之前的定时器,避免重复设置
    if (this.scrollTimeout) {
      clearTimeout(this.scrollTimeout);
    }

    // 设置定时器,200ms 后认为滚动停止
    this.scrollTimeout = setTimeout(() => {
      this.setData({ isScrolling: false });
    }, 200); // 200ms 是一个经验值,可以根据实际情况调整
  },

  onTabClick(e) {
    if (this.data.isScrolling) {
      return; // 如果正在滚动,则不执行点击逻辑
    }

    // 正常的 tab 点击逻辑
    console.log("Tab clicked");
  },

  onUnload() {
    // 页面卸载时清除定时器,防止内存泄漏
    if (this.scrollTimeout) {
      clearTimeout(this.scrollTimeout);
      this.scrollTimeout = null;
    }
  },
});

附二:如何判断两个元素相交

  1. 检测滚动距离

  2. IntersectionObserver api

// js
const app = getApp()

Page({
  data: {
    ratio: ["0%", "0%", "0%", "0%"],
    current: 0
  },
  onLoad() {
    const getCallback = (index) => ({
      intersectionRatio
    }) => {
      console.log(index, intersectionRatio)
      const newRatio = [...this.data.ratio]
      newRatio[index] = Math.floor(intersectionRatio * 100) + "%"
      this.setData({
        ratio: newRatio,
        current: index
      })
    }

    const observe = (selector, callback) => {
      this.createIntersectionObserver({
          thresholds: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
          nativeMode: true,
        })
        .relativeTo(".intersetion")
        .observe(selector, callback)
    }

    observe("#box1", getCallback(0))
    observe("#box2", getCallback(1))
    observe("#box3", getCallback(2))
    observe("#box4", getCallback(3))
  },
})

// html
<view class="fixed" style="left: 0;right: 0;height: 80px;position: fixed;top: 0;background: rgba(255, 0, 0, 0.3);z-index: 9999;">
  fixed 元素;
  相交元素: {{current}}
  <view class="intersetion" style="position: absolute;bottom: 0;left: 0;right: 0;height: 1rpx;background: red;"></view>
</view>
<view class="contents">
  <view class="relative"></view>
  <view class="wrapper">
    <view class="sampleBox" id="box1">
      <view class="label topLeft">{{ratio[0]}}</view>
      <view class="label topRight">{{ratio[0]}}</view>
      <view class="label bottomLeft">{{ratio[0]}}</view>
      <view class="label bottomRight">{{ratio[0]}}</view>
    </view>

    <view class="sampleBox" id="box2">
      <view class="label topLeft">{{ratio[1]}}</view>
      <view class="label topRight">{{ratio[1]}}</view>
      <view class="label bottomLeft">{{ratio[1]}}</view>
      <view class="label bottomRight">{{ratio[1]}}</view>
    </view>

    <view class="sampleBox" id="box3">
      <view class="label topLeft">{{ratio[2]}}</view>
      <view class="label topRight">{{ratio[2]}}</view>
      <view class="label bottomLeft">{{ratio[2]}}</view>
      <view class="label bottomRight">{{ratio[2]}}</view>
    </view>

    <view class="sampleBox" id="box4">
      <view class="label topLeft">{{ratio[3]}}</view>
      <view class="label topRight">{{ratio[3]}}</view>
      <view class="label bottomLeft">{{ratio[3]}}</view>
      <view class="label bottomRight">{{ratio[3]}}</view>
    </view>
  </view>
</view>