Uni-App列表自动滚动

需求

  1. 列表需要自动滚动
  2. 在手动滚动的时候,需要暂停自动滚动
  3. 暂停自动滚动后,3秒内继续自动滚动
  4. 滚动到第50条数据时,回到列表顶部

组件

在项目中采用z-paging组件来处理列表,因此是基于z-paging来实现以上需求

设计思路

难点

自动滚动这个需求本身不复杂,不过有以下几个问题

  1. z-paging组件的scrollY方法不能指定滚动的过渡时间
  2. ··z-paging··组件可以设置usePageScrolltrue,然后当手动执行scrollY的时候,内部会调用uni.pageScrollTo,此方法默认过度时间是300ms,但是不能手动停止

解决思路

  1. 如果要做到平滑过度,那必然需要使用uni的页面滚动,同时为了能及时停止自动滚动,则必须修改z-paging源码,让scroll的时间可以动态传递(这个时间需要不断测试校准,以期达到最好效果)
  2. 手动滚动时,需要停止自动滚动。则需要监听onPageScroll(使用了usePageScroll之后,需要在页面的onPageScroll(e)事件中调用this.$refs.paging.updatePageScrollTop(e.scrollTop);),并且判断 此次偏移量是否是用户触发的(经过校准,可以通过这个条件来判定e.scrollTop - this.scrollTop > 40 || e.scrollToTop - this.scrollTop < -4
  3. 在滚动到50条的时候,回到顶部。则可以通过z-paging组件的listChange获取到第50条数据,结合uni-app的节点布局交互方法createIntersectionObserver监听渲染这条数据的组件是否出现在屏幕上

部分代码实现

核心Template

<!-- z-paging -->
<z-paging
      ref="paging"
      class="offer-warp"
      v-model="dataList"
      :default-page-size="pageSize"
      :usePageScroll="true"
      @listChange="listChange"
    >

<!-- 其他业务代码,其中item-view是列表的cell项 -->
<item-view
          :id="`list-${item.id}`"
          v-for="item in tradingList"
          :data="item"
          :key="item.id"
        />
</z-paging>

核心JavaScript


data() {
    return {
      dataList: [],
      pageSize: 20, //分页条数
      interval: undefined,
      scrollY: 0,
      enableAutoScroll: true,
      itemScrollObserver: undefined,
    };
  },
  
  onShow() {

    this.enableAutoScroll = true;
    this.$nextTick(() => {
      this.startScroll();
    });
  },
  onHide() {
    clearInterval(this.interval);
  },
  onUnload() {
    clearInterval(this.interval);
  },

methods:{
	
    listChange(e) {
      this.enableAutoScroll = true;
      // 滑动到50条的时候,滑动到顶部,重新滚动
      if (e.length >= 50) {
        let item = e[50];
        this.createNodeObsever(item.id);
      }
      this.reloadTimeForScroll(300);
    },
    async getListViewHeight() {
      const query = uni.createSelectorQuery().in(this);
      return new Promise((resolve) => {
        query
          .select('#idRefList')
          .boundingClientRect((data) => {
            console.log('得到布局位置信息' + JSON.stringify(data));
            let height = data.height;
            console.log('节点离页面顶部的距离为' + data.top, this.interval);
            resolve(height);
          })
          .exec();
      });
    },
    async getNodeInfo(section) {
      const query = uni.createSelectorQuery().in(this);
      return new Promise((resolve) => {
        query
          .select(section)
          .boundingClientRect((data) => {
            console.log('getNodeInfo  section' + JSON.stringify(data));
            resolve(data);
          })
          .exec();
      });
    },

    reloadTimeForScroll(holdTime = 5000) {
      clearTimeout(this.timeout);
      this.timeout = setTimeout(() => {
        this.enableAutoScroll = true;
        this.$nextTick(() => {
          this.startScroll();
        });
      }, holdTime);
    },
    /**
     * 1.获取当前组件真实高度height
     * 2.一定时间内往下滑动大概2个cell的高度 step(500毫秒)
     * 3.滑动完成后,判断scrollTop再加上一定高度是否比height更大
     *  3.1 scrollTop + step >=height  再次获取 组件高度,并判断是否滑动
     *  3.2 scrollTop + step < height  继续滑动
     *
     */
    async startScroll() {
      const height = await this.getListViewHeight();
      const step = 6;
      clearInterval(this.interval);
      if (!this.enableAutoScroll) {
        return;
      }

      const time = 1000 / 10;
      this.interval = setInterval(() => {
        let scrollTop = this.scrollTop + step;
        if (!this.enableAutoScroll || height < 800) {
          clearInterval(this.interval);

          return;
        }

        if (scrollTop < height) {
          this.scrollTop = scrollTop;
        }
        uni.pageScrollTo({
          scrollTop: scrollTop,
          duration: time,
        });
      }, time);
    },
    onPageScroll(e) {
       val = { detail: e };
       if (e.scrollTop - this.scrollTop > 40 || e.scrollToTop - this.scrollTop < -4) {
         this.enableAutoScroll = false;
         this.reloadTimeForScroll();
       }
    },

}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值