Loadmore

Loadmore使用的时候分为下拉刷新和底部加载两种方式。css

下拉刷新的时候这样调用:html

<template>
  <div class="page-loadmore">
    <h1 class="page-title">Pull down</h1>
    <p class="page-loadmore-desc">在列表顶端, 按住 - 下拉 - 释放能够获取更多数据</p>
    <p class="page-loadmore-desc">此例请使用手机查看</p>
    <p class="page-loadmore-desc">translate : {{ translate }}</p>

    <div class="loading-background" :style="{ transform: 'scale3d(' + moveTranslate + ',' + moveTranslate + ',1)' }">
      translateScale : {{ moveTranslate }} 
    </div>
    <div class="page-loadmore-wrapper" ref="wrapper" :style="{ height: wrapperHeight + 'px' }">
      <!-- page-loadmore-wrapper元素是loadmore模块的父级盒子,它的高度是绑定了一个响应式的值wrapperHeight -->
      <!-- 在生命周期mounted的时候为page-loadmore-wrapper计算高度 -->
      <!-- page-loadmore-wrapper有一个ref属性,这就是给这个DOM元素添加了一个引用,在当前组件里能够用this.$refs的形式来调用这个DOM元素 -->
      <loadmore :top-method="loadTop" @translate-change="translateChange" @top-status-change="handleTopChange" ref="loadmore">
        <!-- loadmore组件,传进去了一个属性,loadTop会从props接收到 -->
        <!-- loadTop方法用于给列表添加数据项 -->
        <!-- 还给loadmore组件绑定了自定义事件top-status-change,用于更改topStatus这个属性值 -->
        <!-- top-status-change的触发是在loadmore组件内部判断触发的,子组件$emit触发 -->
        <ul class="page-loadmore-list">
          <li v-for="(item, key, index) in list" :key="index" class="page-loadmore-listitem">{{ item }}</li>
        </ul>
        <!-- page-loadmore-list是数据列表 -->
        <div slot="top" class="mint-loadmore-top">
          <span v-show="topStatus !== 'loading'" :class="{ 'is-rotate': topStatus === 'drop' }">↓</span>
          <span v-show="topStatus === 'loading'">
            <a>加载中...</a>
          </span>
        </div>
        <!-- top插槽插入的内容是下拉的时候,数据列表下移后上面出现的箭头和loading文字或者动画 -->
        <!-- 箭头和文字都随着topStatus值来改变显示状态和样式 -->
        <!-- topStatus有三种状态:pull,drop,loading -->
        <!-- loading的时候显示文字或者动画,其它时候显示箭头 -->
      </loadmore>
    </div>
  </div>
</template>

<style lang="scss" scoped>
  .page-loadmore {
    width: 100%;
    overflow-x: hidden;
    .page-loadmore-wrapper {
      margin-top: -1px;
      overflow: scroll;
      .page-loadmore-listitem {
        height: 50px;
        line-height: 50px;
        border-bottom: solid 1px #eee;
        text-align: center;
        &:first-child {
          border-top: solid 1px #eee;
        }
      }
    }
    .loading-background {
      width: 100%;
      height: 50px;
      line-height: 50px;
      text-align: center;
      transition: .2s linear;
    }
    .mint-loadmore-top {
      span {
        display: inline-block;
        transition: .2s linear;
        vertical-align: middle;
      }
      .is-rotate {
        transform: rotate(180deg);
      }
    }
  }
</style>

<script type="text/babel">
  import loadmore from '@/components/loadmore'
  export default {
    data() {
      return {
        list: [],//数据列表
        topStatus: '',//上方loading层状态
        wrapperHeight: 0,//包裹盒子高度
        translate: 0,
        moveTranslate: 0
      };
    },
    methods: {
      handleTopChange(status) {//改变topStatus状态,下方的箭头和加载文字会随着topStatus改变样式或者内容
        this.moveTranslate = 1;
        this.topStatus = status;
      },
      translateChange(translate) {//loadmore组件在滑动时会触发此事件运行此方法
        const translateNum = +translate;
        this.translate = translateNum.toFixed(2);
        this.moveTranslate = (1 + translateNum / 70).toFixed(2);
      },
      loadTop() {//加载更多数据列表
        setTimeout(() => {
          let firstValue = this.list[0];
          for (let i = 1; i <= 10; i++) {
            this.list.unshift(firstValue - i);
          }
          this.$refs.loadmore.onTopLoaded();//加载完数据以后调用loadmore组件的onTopLoaded方法
        }, 1500);
      }
    },
    components: {
      loadmore
    },
    created() {//created的时候先给数据列表里填入20条数据
      for (let i = 1; i <= 20; i++) {
        this.list.push(i);
      }
    },
    mounted() {
      this.wrapperHeight = document.documentElement.clientHeight - this.$refs.wrapper.getBoundingClientRect().top;
      //计算page-loadmore-wrapper的高度
      //html元素的clientHeight - page-loadmore-wrapper盒子距离页面顶部的高度
      //Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置
      //也就是除了页面上面的内容以外,下面整个就是page-loadmore-wrapper盒子,wrapper盒子给一个死高度以后,多给一个overflow:scroll;的样式,这样内容就能够经过滑动看到了
    }
  };
</script>

底部加载的时候这样调用:node

<template>
  <div class="page-loadmore">
    <h1 class="page-title">Pull up</h1>
    <p class="page-loadmore-desc">在列表底部, 按住 - 上拉 - 释放能够获取更多数据</p>
    <p class="page-loadmore-desc">此例请使用手机查看</p>
    <div class="page-loadmore-wrapper" ref="wrapper" :style="{ height: wrapperHeight + 'px' }">
      <!-- page-loadmore-wrapper元素是loadmore模块的父级盒子,它的高度是绑定了一个响应式的值wrapperHeight -->
      <!-- 在生命周期mounted的时候为page-loadmore-wrapper计算高度 -->
      <!-- page-loadmore-wrapper有一个ref属性,这就是给这个DOM元素添加了一个引用,在当前组件里能够用this.$refs的形式来调用这个DOM元素 -->
      <loadmore :bottom-method="loadBottom" @bottom-status-change="handleBottomChange" :bottom-all-loaded="allLoaded" ref="loadmore">
        <!-- loadmore组件,传进去了两个属性,loadmore会从props接收到,loadBottom方法和allLoaded属性 -->
        <!-- loadBottom方法用于给列表添加数据项,allLoaded是个布尔值,判断是否数据已经所有加载完了 -->
        <!-- 还给loadmore组件绑定了一个自定义事件bottom-status-change,用于更改bottomStatus这个属性值 -->
        <!-- bottom-status-change的触发是在loadmore组件内部判断触发的,子组件$emit触发 -->
        <ul class="page-loadmore-list">
          <li v-for="(item, key, index) in list" :key="index" class="page-loadmore-listitem">{{ item }}</li>
        </ul>
        <!-- page-loadmore-list是数据列表 -->
        <div slot="bottom" class="mint-loadmore-bottom">
          <span v-show="bottomStatus !== 'loading'" :class="{ 'is-rotate': bottomStatus === 'drop' }">↑</span>
          <span v-show="bottomStatus === 'loading'">
            <a>加载中...</a>
          </span>
        </div>
        <!-- bottom插槽插入的内容是上拉的时候,数据列表上移后下面出现的箭头和loading文字或者动画 -->
        <!-- 箭头和文字都随着bottomStatus值来改变显示状态和样式 -->
        <!-- bottomStatus有三种状态:pull,drop,loading -->
        <!-- loading的时候显示文字或者动画,其它时候显示箭头 -->
      </loadmore>
      <!-- loadmore组件有三个插槽,top,bottom和默认插槽 -->
      <!-- top是列表下移后上方出现的箭头和loading文字,bottom是上移后下方出现的箭头和文字,默认插槽就是数据列表 -->
    </div>
  </div>
</template>

<style lang="scss" scoped>
    .page-loadmore-listitem {
      height: 50px;
      line-height: 50px;
      border-bottom: solid 1px #eee;
      text-align: center;
    }
    .page-loadmore-wrapper {
        overflow: scroll;
    }
    .mint-loadmore-bottom {
      span {
        display: inline-block;
        transition: .2s linear;
      }
      .is-rotate {
        transform: rotate(180deg);
      }
    }
</style>

<script>
import loadmore from '@/components/loadmore'
export default {
  data () {
    return {
      list: [],//数据列表
      allLoaded: false,//是否所有加载
      bottomStatus: '',//下方loading层状态
      wrapperHeight: 0//包裹盒子高度
    }
  },
  methods: {
    handleBottomChange(status) {//改变bottomStatus状态,下方的箭头和加载文字会随着bottomStatus改变样式或者内容
      this.bottomStatus = status;
    },
    loadBottom() {//加载更多数据列表
      setTimeout(() => {
        let lastValue = this.list[this.list.length - 1];
        if (lastValue < 40) {
          for (let i = 1; i <= 10; i++) {
            this.list.push(lastValue + i);
          }
        } else {
          this.allLoaded = true;//数据所有加载完了,就改变allLoaded
        }
        this.$refs.loadmore.onBottomLoaded();//加载完数据以后调用loadmore组件的onBottomLoaded方法
      }, 1500);
    }
  },
  components: {
    loadmore
  },
  created () {//created的时候先给数据列表里填入20条数据
    for (let i = 0; i <= 20; i++) {
      this.list.push(i)
    }
  },
  mounted () {
    this.wrapperHeight = document.documentElement.clientHeight - this.$refs.wrapper.getBoundingClientRect().top
    //计算page-loadmore-wrapper的高度
    //html元素的clientHeight - page-loadmore-wrapper盒子距离页面顶部的高度
    //Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置
    //也就是除了页面上面的内容以外,下面整个就是page-loadmore-wrapper盒子,wrapper盒子给一个死高度以后,多给一个overflow:scroll;的样式,这样内容就能够经过滑动看到了
  }
}
</script>

loadmore组件:babel

<template>
  <div class="mint-loadmore">
    <!-- mint-loadmore最外层盒子,有overflow:hidden;的样式,这样下方的箭头文字动画或者上方的就会隐藏看不到 -->
    <div class="mint-loadmore-content" :class="{ 'is-dropped': topDropped || bottomDropped}" :style="{ 'transform': transform }">
      <!-- content盒子拥有两个响应式属性,一个在drop的时候添加is-dropped类名,让transform变化更流畅,一个是transform样式,在touchmove的时候,会改变盒子在垂直方向的位置 -->
      <!-- transfrom样式的值是一个计算属性,会随着this.translate变化而变化 -->
      <slot name="top">
        <div class="mint-loadmore-top" v-if="topMethod">
          <span v-if="topStatus === 'loading'" class="mint-loadmore-spinner"></span>
          <span class="mint-loadmore-text">{{ topText }}</span>
        </div>
        <!-- top插槽,列表上拉后下方出现的箭头和loading文字或者动画 -->
        <!-- 当有topMethod这个props传入的时候才显示,此处是备用内容,若是父组件定义了top插槽内容,则备用内容不显示 -->
      </slot>
      <slot></slot>
      <!-- 默认插槽,就是数据列表 -->
      <slot name="bottom">
        <div class="mint-loadmore-bottom" v-if="bottomMethod">
          <span v-if="bottomStatus === 'loading'" class="mint-loadmore-spinner"></span>
          <span class="mint-loadmore-text">{{ bottomText }}</span>
        </div>
      </slot>
      <!-- bottom插槽,列表上拉后下方出现的箭头和loading文字或者动画 -->
      <!-- 当有bottomMethod这个props传入的时候才显示,此处是备用内容,若是父组件定义了bottom插槽内容,则备用内容不显示 -->
    </div>
  </div>
</template>

<style lang="scss" scoped>
  .mint-loadmore {
    overflow: hidden;
  }
  .mint-loadmore-content .is-dropped {
    transition: .2s;
  }
  .mint-loadmore-bottom {
    text-align: center;
    height: 50px;
    line-height: 50px;
    margin-bottom: -50px;
  }
  .mint-loadmore-top {
    text-align: center;
    height: 50px;
    line-height: 50px;
    margin-top: -50px;
  }
</style>

<script type="text/babel">
  export default {
    name: 'loadmore',
    components: {
      
    },
    props: {//props后跟着的对象是验证器,type是类型,default是默认值
      maxDistance: {
        type: Number,
        default: 0
      },
      autoFill: {
        type: Boolean,
        default: true
      },
      distanceIndex: {
        type: Number,
        default: 2
      },
      topPullText: {
        type: String,
        default: '下拉刷新'
      },
      topDropText: {
        type: String,
        default: '释放更新'
      },
      topLoadingText: {
        type: String,
        default: '加载中...'
      },
      topDistance: {
        type: Number,
        default: 70
      },
      topMethod: {
        type: Function
      },
      bottomPullText: {
        type: String,
        default: '上拉刷新'
      },
      bottomDropText: {
        type: String,
        default: '释放更新'
      },
      bottomLoadingText: {
        type: String,
        default: '加载中...'
      },
      bottomDistance: {
        type: Number,
        default: 70
      },
      bottomMethod: {//加载下方数据方法
        type: Function
      },
      bottomAllLoaded: {//布尔值,下方数据已经所有加载
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        translate: 0, //content在y轴移动距离
        scrollEventTarget: null, //scroll元素
        containerFilled: false, //当前滚动的内容是否填充完整
        topText: '', //上方提示文字
        topDropped: false, //下拉刷新是否已经释放
        bottomText: '', //下方提示文字
        bottomDropped: false, //底部加载是否已经释放
        bottomReached: false, //是否已经到达底部
        direction: '', //滑动方向
        startY: 0, //开始滑动的时候触点的的Y坐标
        startScrollTop: 0, //开始滑动的时候,scroll盒子的滚动距离
        currentY: 0, //move过程当中触点的y轴坐标
        topStatus: '', //上方loading层状态,更新后会传给父组件
        bottomStatus: '' //下方loading层状态,更新后会传给父组件
      };
    },
    computed: {
      transform() {//计算属性transform,,根据translate值变化,用于经过transform样式改变content盒子的y轴坐标
        return this.translate === 0 ? null : 'translate3d(0, ' + this.translate + 'px, 0)';
      }
    },
    watch: {
      topStatus(val) {
        //侦听器,若是topStatus发生变化,这个函数就会运行,触发父级组件的事件,并把topStatus新值做为参数传过去
        this.$emit('top-status-change', val);
        switch (val) {
          case 'pull':
            this.topText = this.topPullText;
            break;
          case 'drop':
            this.topText = this.topDropText;
            break;
          case 'loading':
            this.topText = this.topLoadingText;
            break;
        }
        //根据topStatus的新值改变上方的提示文字
      },
      bottomStatus(val) {
      //侦听器,若是bottomStatus发生变化,这个函数就会运行,触发父级组件的事件,并把bottomStatus新值做为参数传过去
        this.$emit('bottom-status-change', val);
        switch (val) {
          case 'pull':
            this.bottomText = this.bottomPullText;
            break;
          case 'drop':
            this.bottomText = this.bottomDropText;
            break;
          case 'loading':
            this.bottomText = this.bottomLoadingText;
            break;
        }
        //根据bottomStatus的新值改变下方的提示文字
      }
    },
    methods: {
      onTopLoaded() {//父级组件里每次加载完新数据就会调用这个方法
        this.translate = 0;//重置this.translate
        setTimeout(() => {
          this.topStatus = 'pull';//数据加载完以后topStatus变为pull状态
        }, 200);
      },
      onBottomLoaded() {//父级组件里每次加载完新数据就会调用这个方法
        this.bottomStatus = 'pull'; //数据加载完以后bottomStatus变为pull状态
        this.bottomDropped = false; //数据加载完以后bottomDropped变为false
        this.$nextTick(() => {//数据变化后会更新DOM,DOM更新后会调用$nextTick()里的方法
          if (this.scrollEventTarget === window) {
            document.body.scrollTop += 50;
          } else {
            this.scrollEventTarget.scrollTop += 50;
          }//数据加载完以后让对应的scroll盒子向下多滚动50px,也就是说多显示一条数据让用户看到
          this.translate = 0;//重置this.translate
        });
        
        if (!this.bottomAllLoaded && !this.containerFilled) {
          this.fillContainer();
        }
      },
      getScrollEventTarget(element) {//获取overflow:scroll的父级盒子
        let currentNode = element;
        while (currentNode && currentNode.tagName !== 'HTML' &&
          currentNode.tagName !== 'BODY' && currentNode.nodeType === 1) {
            //当前传入节点存在且不是html也不是body且是一个元素节点的时候
          let overflowY = document.defaultView.getComputedStyle(currentNode).overflowY;
          //document.defaultView返回document关联的window对象
          //getComputedStyle()获取元素的计算样式
          //overflowY是当前传入节点的计算样式overflow-y
          if (overflowY === 'scroll' || overflowY === 'auto') {
            return currentNode; //若是当前节点的overflow-y值是scroll或者auto,那就返回此节点
          }
          currentNode = currentNode.parentNode;//若是不是,那就获取当前节点的父节点,而后继续判断
        }
        return window;//若是都找不到就返回window对象
      },
      getScrollTop(element) {//获取元素的内容滚动距离
        if (element === window) {
          return Math.max(window.pageYOffset || 0, document.documentElement.scrollTop);
          //window.pageYOffset就是Window.scrollY,文档在垂直方向滚动距离
        } else {
          return element.scrollTop;
        }
      },
      bindTouchEvents() {//为mint-loadmore绑定touch事件操做
        this.$el.addEventListener('touchstart', this.handleTouchStart);
        this.$el.addEventListener('touchmove', this.handleTouchMove);
        this.$el.addEventListener('touchend', this.handleTouchEnd);
      },
      init() {
        this.topStatus = 'pull';//topStatus初始值为pull
        this.bottomStatus = 'pull';//bottomStatus初始值为pull
        this.topText = this.topPullText;
        this.scrollEventTarget = this.getScrollEventTarget(this.$el);
        //获取overflow:scroll的父级盒子
        //传给getScrollEventTarget方法的参数是this.$el,它是当前Vue实例使用的根DOM元素,也就是mint-loadmore
        //this.scrollEventTarget最后获取到是父组件的page-loadmore-wrapper盒子,由于它overflow:scroll;
        if (typeof this.bottomMethod === 'function') {//父级组件传入的加载数据函数若是存在的话
          this.fillContainer();//判断是否数据填充彻底,初始化this.containerFilled的值
          this.bindTouchEvents();//为mint-loadmore绑定touch事件操做
        }
        if (typeof this.topMethod === 'function') {//父级组件传入的加载数据函数若是存在的话
          this.bindTouchEvents();//为mint-loadmore绑定touch事件操做
        }
      },
      fillContainer() {//判断是否数据填充彻底
        if (this.autoFill) {
          this.$nextTick(() => {
            if (this.scrollEventTarget === window) {
              this.containerFilled = this.$el.getBoundingClientRect().bottom >=
                document.documentElement.getBoundingClientRect().bottom;
            } else {
              this.containerFilled = this.$el.getBoundingClientRect().bottom >=
                this.scrollEventTarget.getBoundingClientRect().bottom;
                //若是mint-loadmore的bottom值大于等于滚动盒子的bottom值,说明数据填充彻底了,this.containerFilled为true
            }
            if (!this.containerFilled) {
              this.bottomStatus = 'loading';
              this.bottomMethod();
              //若是数据并无填充彻底,则bottomStatus状态为loading,执行父组件的加载数据方法
            }
          });
        }
      },
      checkBottomReached() {//检查是否已经滑到底部
        if (this.scrollEventTarget === window) {
          /**
           * fix:scrollTop===0
           */
          return document.documentElement.scrollTop || document.body.scrollTop + document.documentElement.clientHeight >= document.body.scrollHeight;
          //若是scroll元素是window的话,就判断文档滑动距离加上文档高度是否大于等于body的内容高度
        } else {
          return parseInt(this.$el.getBoundingClientRect().bottom) <= parseInt(this.scrollEventTarget.getBoundingClientRect().bottom) + 1;
        }
      },
      handleTouchStart(event) {
        this.startY = event.touches[0].clientY;
        //TouchEvent.touches返回全部当前在与触摸表面接触的Touch对象
        //Touch对象表示在触控设备上的触摸点
        //Touch.clientY,触点相对于可见视区上边沿的的Y坐标
        //this.startY是开始滑动的时候触点的Y坐标
        this.startScrollTop = this.getScrollTop(this.scrollEventTarget);
        //开始滑动的时候,scroll盒子的滚动距离
        this.bottomReached = false;
        //是否已经滑动到底部
        if (this.topStatus !== 'loading') {//若是上方提示块并未处于加载阶段就重置topStatus和topDropped
          this.topStatus = 'pull';
          this.topDropped = false;
        }
        if (this.bottomStatus !== 'loading') {//若是下方提示块并未处于加载阶段就重置bottomStatus和bottomDropped
          this.bottomStatus = 'pull';
          this.bottomDropped = false;
        }
      },
      handleTouchMove(event) {
        if (this.startY < this.$el.getBoundingClientRect().top && this.startY > this.$el.getBoundingClientRect().bottom) {
          return;
        }
        //若是触点在mint-loadmore以外就退出move事件
        this.currentY = event.touches[0].clientY;
        //this.currentY是move过程当中触点的y轴坐标
        let distance = (this.currentY - this.startY) / this.distanceIndex;
        //滑动的距离
        this.direction = distance > 0 ? 'down' : 'up';
        //判断滑动方向
        if (typeof this.topMethod === 'function' && this.direction === 'down' &&
          this.getScrollTop(this.scrollEventTarget) === 0 && this.topStatus !== 'loading') {
          //若是滑到了顶部
          event.preventDefault();//阻止默认事件
          event.stopPropagation();//阻止事件冒泡
          if (this.maxDistance > 0) {
            this.translate = distance <= this.maxDistance ? distance - this.startScrollTop : this.translate;
          } else {
            this.translate = distance - this.startScrollTop;
            //随着滑动来更新translate值,translate值变化,计算属性transform就随之变化,content盒子就在y轴上向下移动
          }
          if (this.translate < 0) {//刚滑到顶部滑不动,会顿一下
            this.translate = 0;
          }
          this.topStatus = this.translate >= this.topDistance ? 'drop' : 'pull';
          //topDistance默认70,拉动距离超过70下方箭头就变个方向
        }
        if (this.direction === 'up') {//若是是向上滑动,那就是底部加载,就判断是否已经滑到底部
          this.bottomReached = this.bottomReached || this.checkBottomReached();
        }
        if (typeof this.bottomMethod === 'function' && this.direction === 'up' &&
          this.bottomReached && this.bottomStatus !== 'loading' && !this.bottomAllLoaded) {
          //若是拉到底部了且数据没有加载完
          event.preventDefault();//阻止默认事件
          event.stopPropagation();//阻止事件冒泡
          if (this.maxDistance > 0) {
            this.translate = Math.abs(distance) <= this.maxDistance
              ? this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance : this.translate;
          } else {
            this.translate = this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance;
            //随着滑动来更新translate值,translate值变化,计算属性transform就随之变化,content盒子就在y轴上向上移动
          }
          if (this.translate > 0) {//刚滑到底部滑不动,会顿一下
            this.translate = 0;
          }
          this.bottomStatus = -this.translate >= this.bottomDistance ? 'drop' : 'pull';
          //bottomDistance默认70,拉动距离超过70下方箭头就变个方向
        }
        this.$emit('translate-change', this.translate);//触发父组件事件,这个是上拉刷新的时候用的
      },
      handleTouchEnd() {
        if (this.direction === 'down' && this.getScrollTop(this.scrollEventTarget) === 0 && this.translate > 0) {
          //下拉刷新
          this.topDropped = true;//drop状态变动,content添加is-dropped样式,回到原点动画
          if (this.topStatus === 'drop') {
            this.translate = '50';
            this.topStatus = 'loading';
            this.topMethod();
            //若是topStatus仍是drop状态,说明刚放手,那就让content回到距离顶部50px的地方,而后改变topStatus为loading,而后执行父组件加载新数据的方法
          } else {
            this.translate = '0';
            this.topStatus = 'pull';
            //若是没有从超过70的地方释放,那就回到初始状态,不加载新数据
          }
        }
        if (this.direction === 'up' && this.bottomReached && this.translate < 0) {
          //底部加载
          this.bottomDropped = true;//drop状态变动,content添加is-dropped样式,回到原点动画
          this.bottomReached = false;//改变是否到达底部状态
          if (this.bottomStatus === 'drop') {
            this.translate = '-50';
            this.bottomStatus = 'loading';
            this.bottomMethod();
            //若是bottomStatus仍是drop状态,说明刚放手,那就让content回到距离底部50px的地方,而后改变bottomStatus为loading,而后执行父组件加载新数据的方法
          } else {
            this.translate = '0';
            this.bottomStatus = 'pull';
            //若是没有从超过70的地方释放,那就回到初始状态,不加载新数据
          }
        }
        this.$emit('translate-change', this.translate);//触发父组件事件,这个是上拉刷新的时候用的
        this.direction = '';//清空方向
      }
    },
    mounted() {//mounted的时候调用init()初始化组件状态
      this.init();
    }
  };
</script>

实现原理就是,外面有个wrapper盒子有死高度且拥有样式overflow:scroll;的样式,这样它的内容超出后就是可滚动的,它的滚动高度scrollTop就能够拿来计算用。wrapper盒子里的内容除了数据列表之外还有一个loading层,这个loading层就是已经到顶部再继续下拉 或者 已经到底部再上拉的时候才会显示出来,平时的时候利用maring负值改变y轴方向的位置隐藏起来,它里面就是loading动画和一个小箭头提示标志,滑动的时候改变里面content盒子的y轴坐标,而后将loading层显示出来,释放的时候让content盒子回到距离原位一个loading层高度的地方,而后发请求加载数据,等数据加载好了再次把全部DOM的状态回归到默认状态。app

相关文章
相关标签/搜索