vue2.0 自定义 下拉刷新和上拉加载更多(Scroller) 组件

Posted 每天都要进步一点点

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue2.0 自定义 下拉刷新和上拉加载更多(Scroller) 组件相关的知识,希望对你有一定的参考价值。

1.下拉刷新和上拉加载更多组件

Scroller.vue

<!-- 下拉刷新 上拉加载更多 组件 -->
<template>
  <div
    :style="marginTop"
    class="yo-scroll"
    :class="{\'down\':(state===0),\'up\':(state==1),refresh:(state===2),touch:touching}"
    @touchstart="touchStart($event)"
    @touchmove="touchMove($event)"
    @touchend="touchEnd($event)">
    <section class="inner" :style="{ transform: \'translate3d(0, \' + top + \'px, 0)\' }">
      <!-- 顶部提示语(刷新) -->
      <header class="pull-refresh">
        <slot name="pull-refresh">
          <span class="down-tip">下拉更新</span>
          <span class="up-tip">松开刷新数据</span>
          <span class="refresh-tip">加载中……</span>
        </slot>
      </header>
      <!-- 父组件 列表部分 -->
      <slot>
      </slot>
      <!-- 底部提示语(加载更多) -->
      <footer class="load-more">
        <slot name="load-more">
          <span v-show="downFlag === false">上拉加载更多</span>
          <span v-show="downFlag === true">加载中……</span>
        </slot>
      </footer>
      <!-- 暂无数据提示语 -->
      <div class="nullData" v-show="dataList.noFlag">暂无更多数据</div>
    </section>
  </div>
</template>

<script>
  export default {
    // 接收父组件参数
    props: {
      marginTop: {
        default: "margin-top:40px;"
      },
      // 默认高度
      offset: {
        type: Number,
        default: 100
      },
      // 是否支持加载更多
      enableInfinite: {
        type: Boolean,
        default: true
      },
      // 是否支持刷新
      enableRefresh: {
        type: Boolean,
        default: true
      },
      // 是否显示\'暂无数据\'
      dataList: {
        default: false,
        required: false
      },
      // 刷新方法
      onRefresh: {
        type: Function,
        default: undefined,
        required: false
      },
      // 加载更多方法
      onInfinite: {
        type: Function,
        default: undefined,
        require: false
      }
    },
    data() {
      return {
        top: 0, // 下拉高度
        state: 0, // 状态: 0 下拉/ 1 上拉 / 2 刷新
        startX: 0, // 手指滑动起始位置 X轴
        startY: 0, // 手指滑动起始位置 Y轴
        touching: false, // -webkit-overflow-scrolling
        infiniteLoading: false, // 加载更多效果
        downFlag: false, //用来显示是否加载中
      }
    },
    methods: {
      // 手指刚开始滑动
      touchStart(e) {
        this.startY = e.targetTouches[0].pageY;
        this.startX = e.targetTouches[0].pageX;
        this.startScroll = this.$el.scrollTop || 0;
        this.touching = true; //留着有用,不能删除
        this.dataList.noFlag = false; // 默认 不显示\'暂无数据\'
        this.$el.querySelector(\'.load-more\').style.display = \'block\';// 实体化加载更多
      },
      // 手指移动中
      touchMove(e) {
        if(!this.enableRefresh || this.dataList.noFlag || !this.touching) {
          return
        }
        let diff = e.targetTouches[0].pageY - this.startY - this.startScroll
        if(diff > 0) e.preventDefault()
        this.top = Math.pow(diff, 0.8) + (this.state === 2 ? this.offset : 0)
        if(this.state === 2) { // 刷新中
          return
        }
        if(this.top >= this.offset) {
          this.state = 1
        } else {
          this.state = 0
        }
        let more = this.$el.querySelector(\'.load-more\');
        if(!this.top && this.state === 0) {
          more.style.display = \'block\';
        } else {
          more.style.display = \'none\';
        }
      },
      // 手指结束滑动
      touchEnd(e) {
        if(!this.enableRefresh) {
          return
        }
        this.touching = false
        if(this.state === 2) { // 刷新中
          this.state = 2
          this.top = this.offset
          return
        }
        if(this.top >= this.offset) { // 进行刷新
          this.refresh()
        } else { // 取消刷新
          this.state = 0
          this.top = 0
        }
        //用于判断滑动是否在原地 ----begin
        let endX = e.changedTouches[0].pageX,
          endY = e.changedTouches[0].pageY,
          dy = this.startY - endY,
          dx = endX - this.startX;
        //如果滑动距离太短
        if(Math.abs(dx) < 2 && Math.abs(dy) < 2) {
          console.log("滑动距离太短")
          return;
        }
        //--------end--------
        if(!this.enableInfinite || this.infiniteLoading) {
          return
        }
        let outerHeight = this.$el.clientHeight,
          innerHeight = this.$el.querySelector(\'.inner\').clientHeight,
          scrollTop = this.$el.scrollTop,
          ptrHeight = this.onRefresh ? this.$el.querySelector(\'.pull-refresh\').clientHeight : 0,
          bottom = innerHeight - outerHeight - scrollTop - ptrHeight;
        if(bottom <= this.offset && this.state === 0) {
          this.downFlag = true;
          this.infinite();
        } else {
          this.$el.querySelector(\'.load-more\').style.display = \'none\';
          this.downFlag = false;
        }
      },
      // 刷新
      refresh() {
        this.state = 2;
        this.top = this.offset;
        setTimeout(() => {
          this.onRefresh(this.refreshDone)
        }, 300);
      },
      // 结束刷新
      refreshDone() {
        this.state = 0
        this.top = 0
      },
      // 加载更多
      infinite() {
        this.infiniteLoading = true
        setTimeout(() => {
          this.onInfinite(this.infiniteDone);
        }, 2000);
      },
      // 结束加载更多
      infiniteDone() {
        this.infiniteLoading = false
      }
    }
  }
</script>

<style lang="less" scoped>
  .yo-scroll {
    // margin-top: 40px; // 解决有标题栏的bug
    font-size: 16px;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    overflow: auto;
    z-index: 100;
    height: auto;
    -webkit-overflow-scrolling: touch;
    .inner {
      position: absolute;
      top: -50px;
      width: 100%;
      height: auto;
      transition-duration: 300ms;
      .pull-refresh {
        position: relative;
        left: 0;
        top: 0;
        width: 100%;
        height: 50px;
        display: flex;
        display: -webkit-flex;
        align-items: center;
        justify-content: center;
      }
      .load-more {
        height: 50px;
        line-height: 50px;
        display: flex;
        text-align: center;
        align-items: center;
        justify-content: center;
        display: none;
      }
      .nullData { //暂无更多数据样式
        font-size: 16px;
        color: #999999;
        height: 50px;
        line-height: 60px;
        text-align: center;
      }
      .down-tip,
      .refresh-tip,
      .up-tip {
        display: none;
      }
      .up-tip:before,
      .refresh-tip:before {
        content: \'\';
        display: inline-block;
        width: 110px;
        height: 40px;
        font-size: 16px;
        background-size: 70% !important;
        position: absolute;
        top: 0;
        left: 20%;
      }
      .up-tip:before {
        background: url(../assets/images/down-logo.png) no-repeat left;
      }
      .refresh-tip:before {
        background: url(../assets/images/refresh-logo.gif) no-repeat left;
      }
    }
  }

  .yo-scroll.touch .inner {
    transition-duration: 0;
  }

  .yo-scroll.down .down-tip {
    display: block;
  }

  .yo-scroll.up .up-tip {
    display: block;
  }

  .yo-scroll.refresh .refresh-tip {
    display: block;
  }
</style>

2.页面调用

LoadMore.vue

<!-- 加载更多 -->
<template>
  <div>
    <!-- 标题栏 -->
    <mt-header title="加载更多">
      <router-link to="/" slot="left">
        <mt-button icon="back">返回</mt-button>
      </router-link>
    </mt-header>
    <!-- 列表 -->
    <div class="cont">
      <m-scroller :on-refresh="onRefresh" :on-infinite="onInfinite" :dataList="scrollData" :marginTop="marginTop">
        <ul>
          <li v-for="(item,index) in listdata">{{item.name}}</li>
        </ul>
      </m-scroller>
    </div>
  </div>
</template>

<script>
  import mScroller from \'../components/Scroller\'

  export default {
    components: {
      mScroller
    },
    data() {
      return {
        marginTop:\'margin-top:40px;\',
        pageStart: 0, // 开始页数
        pageEnd: 0, // 结束页数
        listdata: [], // 数据列表
        scrollData:{
          noFlag: false //暂无更多数据显示
        }
      }
    },
    mounted: function() {
      // 首次请求数据
      this.fetchData();
    },
    methods: {
      fetchData() {
        this.axios.get(\'/api/testData\').then((response) => {
          this.listdata = response.data.data.list;
          // 获取总页数
          this.pageEnd = response.data.data.totalPage;
          // 还原
          this.pageStart = 0;
        })
      },
      // 下拉刷新
      onRefresh(done) {
        this.fetchData();
        done(); // call done
      },
      // 上拉加载更多
      onInfinite(done) {
        this.pageStart++;
        // 加载条
        let more = this.$el.querySelector(\'.load-more\');
        // 判断是否显示加载条
        if(this.pageStart > this.pageEnd){
          //走完数据调用方法
          this.scrollData.noFlag = true;
        }else{
          let _this = this;
          this.axios.get(\'/api/testData\').then((response) => {
            _this.listdata = _this.listdata.concat(response.data.data.list);
            // 获取总页数
            _this.pageEnd = response.data.data.totalPage;
          })
        }
        // 隐藏加载条
        more.style.display = \'none\';
        done();
      }
    }
  }
</script>

<style lang="less" scoped>
  ul {
    li {
      min-height: 50px;
      line-height: 50px;
      text-align: center;
      border: 1px solid red;
    }
  }
  // 隐藏滚动条
  ::-webkit-scrollbar{
    display:none;
  }
</style>

 

3.效果图

 

以上是关于vue2.0 自定义 下拉刷新和上拉加载更多(Scroller) 组件的主要内容,如果未能解决你的问题,请参考以下文章

vue2.0 移动端,下拉刷新,上拉加载更多插件,修改版

Android实现RecyclerView的下拉刷新和上拉加载更多

vue2.0 移动端,下拉刷新,上拉加载更多 插件

使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多

使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多

使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多