代码优化-无限循环列表


无限循环列表Vue实现

为了实现上次的承诺点开这里,我分装了一个组件,主要的功能是:

  1. 自动滚动
  2. 无限循环
  3. 不断请求数据

系统里面有类似的组件(基于happy-scroll实现),但是除了不太好用之外,功能上没有实现无限循环以及只能展示固定的数据。

演示地址,这个演示只能实现单向滚动,跟我要实现的不太一样,不过莫急,看👇

好像不是很直观,可以看右边的滚动条,到了顶部和底部会自动加载数据数据,滑到顶部会自动滚动到请求数据的第一条数据,看下图

使用

组件名:esay-auto-scroll-list

属性:

属性
width 设置组件的宽
height 设置组件的高
haveData 状态,是否有数据
loading 状态,是否在加载数据(请求数据是否返回)
flag 每个ul的唯一标志,ul必须增加data-flag的自定义标签
notice 当滚到底/顶部的回调函数

注意点

  1. 组件只是实现数据的滚动,以及滚动到边界(顶/底)调用回调函数,具体要实现无限上下滚动 or 单向有限滚动 or 其他,需要回调函数的配合(在线实例和博客动图就是回调函数的不同,效果就不一样)
  2. haveData和loading是根据实际返回数据设置的。在发送请求前,havaData=false loading=true。请求结束后,haveData根据实际情况设置,loading=false

组件

组件源码,注意也要使用config.js配置文件,在项目总请使用 autoScrollList实际使用版本.vue

知识点

clientHeight

跟元素的滚动和位置无关,代表元素的高度,包括padding但不包括border、水平滚动条、margin的元素的高度。对于inline的元素这个属性一直是0,单位px,只读属性

offsetHeight

跟元素的滚动和位置无关,代表元素的高度,包括padding、border、水平滚动条,但不包括margin的元素的高度。对于inline的元素这个属性一直是0,单位px,只读属性

scrollHeight

跟滚动条有关(子元素高于父元素),代表当前不可见部分的高度,可见部分就是clientHeight,单位px,只读属性

判断是否滑到底部: ele.scrollHeight - ele.scrollTop ==== ele.clientHeight

scrollTop

代表在有滚动条时,滚动条向下滚动的距离也就是元素顶部被遮住部分的高度。在没有滚动条时scrollTop==0恒成立。单位px,可读可写属性

offsetTop

它返回当前元素相对于其 offsetParent 元素的顶部的距离(子边框外侧到父边框的内存的距离),单位px,只读属性

clientTop

一个元素顶部边框的宽度,不包括顶部外边距或内边距。可以理解成定义border的高度,单位px,只读属性

基本思路

上滑顶部:当发出请求前(调用回调函数前),保存当前列表中的第一条数据以及scrollTop和offetHeight。当请求结束后,更新完数据,将保存的数据插入到最新数据的后面,并更新scrollTop保持视觉上的连续性。

下滑到底部:当发出请求前(调用回调函数前),保存当前列表最后一条数据以及scrollTop和offetHeight。当请求结束后,更新完数据,将保存的数据插入到最新数据的前面,并更新scrollTop保持视觉上的连续性。

注意点

  1. 保存的的offsetHeight是多个子元素的offsetHeight的集合
  2. 使用insertBefore插入的元素,在下一次更新数据时,可能还会保存,需要去除多余的子元素(唯一标志flag的作用)
  3. 最后一组数据比较特殊,数量可能没有最开始请求那么多。例如设置的请求数量是10条,但最后一组数据可能只有1条,需要特殊处理。

回调函数

回调函数是实现需要效果的关键,就是看你怎么处理边界位置的情况,下面是我的实例:

async  loadData(type){
            this.loading = true
            this.moreData = false 
            this.flag++
            let page 
            if(type === scrollDir.down){
             if(this.endPage === this.totalPage){
                 this.endPage = 1
                 this.startPage = this.totalPage
             }else{
                const newStartPage = (this.startPage+1)%this.totalPage
                const newEndPage = (this.endPage+1)%this.totalPage
                this.endPage = newEndPage===0? this.totalPage: newEndPage
                this.startPage = this.endPage%this.totalPage - 1>0? this.endPage%this.totalPage - 1: this.totalPage
             }
             page = this.endPage
           }else if(type === scrollDir.up){
               if(this.startPage<=1){
                   this.startPage = this.totalPage
                   this.endPage = 1
               }else{
                   const newStartPage = this.startPage-1
                   const newEndPage = this.endPage-1
                   this.startPage = newStartPage>0? newStartPage:1
                   this.endPage = this.startPage%this.totalPage + 1
               }
               page = this.startPage
           }
            this.investProjectList = await this.getInvestProjectsData({page})
            this.totalPage = this.investProjectList.pages
            this.moreData = true
            this.loading = false
        },

注意点

  1. 需要定义startPage 和 endPage两个变量跟踪page变化,之所以要两个变量是因为当前可视窗口可以存在多个子元素

  1. 上滑endPage根据startPage变化,下滑startPage根据endPage变化

总结

这次实现这个功能用的时间太长了,写到一半的时候发现不行,又推到重来了,浪费了很多时间,会这样还是因为自己前期没有考虑仔细就开始写代码,连写什么东西都不明确,需要反思。


文章作者: 木叶勇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 木叶勇 !
  目录