手写uniapp上滑底部刷新(非触底加载)

923 阅读3分钟

最近在用uniapp写小程序时接到了一个需求,下滑刷新跳转,写好之后大概如下图。

image.png

可以看出,这个功能其实和小程序自带的下拉刷新非常像,但它是到底部才能继续上滑加载,作者在uniapp的社区也寻找了一段时间的插件,但没有找到很符合的,在网上进行搜索也是大片的基础触底加载下拉刷新之类的文章,所以决定自己手搓一个,故有此文。

首先来分析一下我们该怎么做,既然是上滑,那就势必要监听用户的滑动操作,这点uniapp有提供事件,我们只需要把他们绑定在父元素上就好,我这里是绑定到了最大的父元素上。

<view class="read" @touchstart="start" @touchend="end" @touchmove="move">

这三个事件分别是用户手指触碰屏幕,在屏幕上滑动,以及离开屏幕的事件,他们都会携带一下信息,比如我们需要的坐标信息。

拿到这些信息之后,就可以将他们先存起来,方便后面分析。(这里用的是vue2)

        touches: {
          startY: 0, //起始Y轴坐标
          moveY: 0, //移动过程Y轴坐标
          refreshHeight: 0, //下拉刷新的距离 用于赋值到translate rpx
          refreshTargetHeight: 300, //下拉刷新的目标值 rpx
          maxRefreshHeight: 300 //最大下拉距离  包括减缓距离 rpx
        }

然后是几个滑动的事件函数和一个判断是否是上滑手势的函数

methods: {
          //判断是否为下拉手势
      isPullDown() {
        let {
          startY,
          moveY
        } = this.touches
        if (moveY < startY) {
          return true
        }
        return false
      },
      start(e) {
        this.touches.startY = e.touches[0].pageY
      },
      move(e) {
        let {
          refreshHeight,
          refreshTargetHeight,
          maxRefreshHeight
        } = this.touches
        //超出最大边界值 跳出
        if (refreshHeight >= maxRefreshHeight) return;
        // 记录移动过程中的 Y 轴坐标
        this.touches.moveY = e.touches[0].pageY
        let {
          startY,
          moveY
        } = this.touches
        //计算下拉差值
        let distance = startY - moveY
        //到达目标值后  下拉距离减缓
        if (refreshHeight >= refreshTargetHeight) {
          this.touches.refreshHeight = refreshHeight + (distance - refreshHeight) / 10
          return
        }
        //符合下拉 动态赋值translate
        if (this.isPullDown()) {
          this.touches.refreshHeight = distance
        }
      },
      async end(e) {
        if (!(this.is_down && (this.is_novel_end || this.ai_data.length > 7))) return
        if (this.isPullDown()) {
          let {
            refreshHeight,
            refreshTargetHeight
          } = this.touches
          //未到达目标值
          if (refreshHeight < refreshTargetHeight) {
            this.touches.refreshHeight = 0
            console.log('未到达目标值 回弹')
            return
          }
          //模拟加载数据
          console.log('加载数据中...')
          // await (你要执行的操作)
          this.touches.refreshHeight = 0
        }
      },
}

这样js的部分差不多就写完了,接下来是滑动时页面和底部加载区域的位移效果。

首先制作一个底部的加载区域。

    <view class="top_refresh">
      <uni-icons type="spinner-cycle" size="60"></uni-icons>
      <text>继续上滑,开启新冒险~</text>
    </view>

然后使用fixed绝对定位将它定位在底部,再用transform把它移出屏幕外。

    .top_refresh {
      position: fixed;
      left: 0;
      padding-bottom: 130rpx;
      transform: translateY(160rpx);
      height: 300rpx;
      width: 100vw;
      background-color: rgba(244, 249, 246);
     }

接下来是位移效果,首先是在你希望在滑动时产生位移效果的元素上添加

<view class="read_content" id="read_content" :style="{transform:'translateY(' + -touches.refreshHeight + 'rpx);'}">

然后是在底部加载区域上添加,

    <view class="top_refresh" :style="{bottom: (touches.refreshHeight/1.5) + 'rpx'}">
      <uni-icons type="spinner-cycle" size="60"></uni-icons>
      <text>继续上滑,开启新冒险~</text>
    </view>

这样大体就完成了,但是我这里出了一个小问题,我的页面是不断向下加载的,只有加载完毕才应该开启上滑刷新,同时在页面高度很高的情况下,应该到达底部才能开启上滑刷新,这里因为是uniapp,无法获取DOM,可以通过uni.createSelectorQuery()来获取元素节点,同时通过onPageScroll监听页面的滑动,这两个api详见uniapp官方文档。

    onPageScroll(res) {
      const query = uni.createSelectorQuery().in(this);
      query.select('#read_content').boundingClientRect(data => {
        this.is_down = data.height + data.top > 800 ? false : true
      }).exec();
    }

然后通过节点的top值和height值判断是否到了底部,这里我很粗糙的写了一个800作为判断依据,实际上应该是手机的视口高度,然后用this.is_down来接收到结果,如果is_down值不为true,并且页面数据没有加载完毕时就不执行监听用户滑动的事件,也就是在三个事件中加上:

if (!(this.is_down && this.loading)) return

至此这个小功能就制作完毕了,新手第一次发文,如有不足,感谢斧正。