vue手动实现移动端下拉刷新上滑加载更多

2022-01-05  本文已影响0人  变量只提升声明不提升赋值

先上代码

<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8' />
  <title>mobile分页</title>
  <script src='https://unpkg.com/vue/dist/vue.js'></script>
  <style>
      html {
          font-size: 40px;
      }

      * {
          margin: 0;
          padding: 0;
      }

      .box {
          overflow: hidden;
          position: relative;
          width: 100%;
          height: 100vh;
          display: flex;
      }

      .navBox {
          overflow-y: auto;
          width: 20%;
          border-right: 1px solid #ddd;
          height: 100%;
      }

      .nav_item {
          padding: 20px 0;
          text-align: center;

      }

      .item_box {
          position: absolute;
          top: 0;
          right: 0;
          overflow-y: auto;
          width: 80%;
          height: 100%;
      }

      .item {
          width: 100%;
          height: 350px;
      }

      .item:nth-child(1n) {
          background: yellow;
      }

      .item:nth-child(2n) {
          background: #13c2c2;
      }

      .item:nth-child(3n) {
          background: #159b76;
      }

      .item:nth-child(4n) {
          background: orange;
      }

      .loadingBox {
          padding: 20px;
          text-align: center;
      }
  </style>
</head>
<body>
<div id='app'>
  <div class='box'>
    <div class='navBox'>
      <div class='nav_item' v-for='(item,index) in navList' :key='index'>{{item.name}}</div>
    </div>
    <div class='item_box'  :style="{'transition':`all .${number}s`,'top':`${translateY}px`}" ref='scroll' id='box' @touchend='touchend' @touchmove='touchmove' @touchstart='touchstart'>
      <div class='loadingBox' v-if='touchstartTitleShow'>释放可刷新...</div>
      <div class='loadingBox' v-if='touchEndTitleShow'>加载中...</div>
      <div class='item' v-for='(itme,index) in list' :key='index'>
        {{index}}
      </div>
      <div class='loadingBox' v-if='loading'>加载中...</div>
      <div class='loadingBox' v-if='!loading&&!hasNext'>已加载全部内容</div>
    </div>
  </div>
</div>
<script type='text/javascript'>
  new Vue({
    el: '#app',
    data: {
      touchEndTitleShow:false, //控制手指离开屏幕的title显示
      touchstartTitleShow:false,//控制手指按下屏幕的title显示
      number:0,//列表回弹动画时间
      translateY:0,//列表随手指下拉而偏移的量
      startY:0,//手指按住的位置的y坐标,也就是起始坐标
      hasNext: true,//是否还有下一页
      loading: false,//loading显示
      navList: [
        {
          name: '分类1'
        },
        {
          name: '分类2'
        },
        {
          name: '分类3'
        },
        {
          name: '分类4'
        },
        {
          name: '分类5'
        },
        {
          name: '分类6'
        }
      ],
      list: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    },
    mounted() {
      this.initScrollChange()
    },
    methods: {
      //手指触碰到屏幕
      touchstart(e) {
        this.number = 0
        let y = e.targetTouches[0].pageY
        this.startY = y
      },
      //手指开始滑动
      touchmove(e) {
        let y = e.targetTouches[0].pageY
        if((y>this.startY)&&this.$refs.scroll.scrollTop==0){
          this.touchstartTitleShow = true
          //如果当前移动距离大于初始点击坐标,则视为是下拉。并且要处于顶部才刷新,不能影响正常的列表滑动。
          this.translateY = (y-this.startY)/2
        }
      },
      //手指松开
      touchend(e){
        let y = e.changedTouches[0].pageY
       if(y>this.startY){
         this.number = 4
         this.translateY = 0
         this.touchstartTitleShow = false
         this.touchEndTitleShow = true
         setTimeout(()=>{
           this.touchEndTitleShow = false
         },1000)
         this.startY = 0
       }
      },
      initScrollChange() {
        this.$refs.scroll.onscroll = (e) => {
          const offsetHeight = this.$refs.scroll.offsetHeight //可视区域的高度
          const scrollHeight = this.$refs.scroll.scrollHeight //元素全部高度
          const scrollTop = this.$refs.scroll.scrollTop //滚动条滚动距离
          //可视区域高度加上滚动条滚动距离大于等于元素全部高度则表示滚动到底
          if ((offsetHeight + scrollTop) - scrollHeight >= -1) {
            console.log('到底啦')
            if (!this.loading && this.hasNext) {
              this.getData()
            }
          }
        }
      },
      getData() {
        this.loading = true
        setTimeout(() => {
          for (let i = 0; i < 10; i++) {
            this.list.push(this.list.length + 1)
          }
          this.loading = false
          if (this.list.length > 30) {
            this.hasNext = false
          }
        }, 1000)
      }
    }
  })
</script>
</body>
</html>

效果图


image.png

demo里实现的是左右分布的列表,上下分布的代码一致,改一下布局即可

代码解析:
首先实现加载更多,这个比较容易。要实现此功能,我们就必须得知道我们的元素的滚动条什么时候滚到底部了,只有滚到底部我们才去拉下一页数据

vue里提供了ref来帮助我们获取元素,元素身上有个onscroll 方法,此方法可监听滚动条的滚动

  this.$refs.scroll.onscroll = (e) => {
          const offsetHeight = this.$refs.scroll.offsetHeight     可视区域的高度
          const scrollHeight = this.$refs.scroll.scrollHeight     元素全部高度
          const scrollTop = this.$refs.scroll.scrollTop           滚动条滚动距离

         可视区域高度加上滚动条滚动距离大于等于元素全部高度则表示滚动到底

          if ((offsetHeight + scrollTop) - scrollHeight >= -1) {
            console.log('到底啦')
在这里我们就可以去加载下一页了,但是并不是无脑加载。是有条件的,
只有当loading为false的时候并且有下一页,我们才去加载。以免出现用户一直下滑,我们的程序反复的出现loading
            if (!this.loading && this.hasNext) {
              this.getData()
            }
          }
        }

getData函数这里就不过多描述了

这样一个简单的上滑加载更多就实现了。

现在来解析一下下拉刷新

首先上拉刷新,那我们肯定是要监听用户的手触碰屏幕的事件的。
这里vue也提供了以下事件


image.png

在手指刚触碰到屏幕的时候,我们需要记录下当前的y坐标,以便后续用来作比较分变出此时是上滑还是下拉

在移动的函数里去比较其实坐标和当前坐标的大小,如果是下拉。则动态的修改整个list盒子的top值。

手指松开后就是重置一些东西,但是这里要注意。用户下滑的时候也是会触发这些事件的,所以在松开的函数里,也需要做一个判断,如果是上滑后松开那就不做任何处理,如果是下拉后松开,那就重置一些东西。

具体的实现也不难,上述demo只是一个精简版,但整体思路就是这样。

上一篇下一篇

猜你喜欢

热点阅读