vue 仿B站下拉刷新上拉加载

vue 仿B站下拉刷新上拉加载

功能大部分都是跟B站一样的,还是有一些瑕疵和小bug的,φ(>ω<*)
先上demo连接和gitHub项目地址吧
demo展示
https://github.com/Micoki/vue-refresh

pull-refresh.vue 所有功能代码都在这里面直接创建全局组件就可以使用

import pullRefresh from './components/pull-refresh'    //加载全局组件
Vue.component('pull-refresh',pullRefresh) 

使用方法

<template>
  <div class="hello">
      <pull-refresh v-model="refresh"
      :pullColors="'red'"
      :scrollHide="true"
      :finished="finishends"
      finishedText="到底了,没有啦!"
      @refresh="refershs"
      @load="loads">
          <p v-for="a in refreshs">{{a}}</p>
          <p v-for="a in 30">加载!加载!加载!</p> 
          <p v-for="a in dates">{{a}}</p>
      </pull-refresh>
  </div>
</template>

<script> 
 // 防抖   配合 上拉加载数据
function debounce(fn, delay) {    
    var delay = delay || 300;
    var timer;
    return function () {
        var th = this;            //设置this指向
        var args = arguments;     //设置伪数组对象指向
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
            timer = null;
            fn.apply(th, args);
        }, delay);      //回调时间
    }; 
} 
export default {
  name: 'HelloWorld',
  data () {
    return {
       refresh:true,
       finishends:true,
       refreshs:[Math.random()*1134,Math.random()*1224,Math.random()*3134,Math.random()*1100],
       dates:['下拉加载'],
       num:0
    }
  },
  methods: {
      refershs(){
          setTimeout(()=>{
            this.refreshs = [Math.random()*1134,Math.random()*1224,Math.random()*3134,Math.random()*1100]
            this.refresh = false
          },3000)
      },
      // 建议配合防抖进行上拉加载数据
      loads:debounce(function(){
        this.num++
        if(this.num > 4){
          this.finishends = false
        }
        console.log('上拉加载成功!')
        this.dates.push('在上啦就加载','继续继续继续','上拉下拉','在上啦就加载','继续继续继续','上拉下拉',)
      },1000) 
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
 .hello{
   width: 100%; height: 100%;
 }
</style>

里面功能代码我都做了说明,很容易理解的,我就把js代码贴出来,全部代码建议down我的github下来查看(~ ̄▽ ̄)~

js代码

<script>  
/**
 * api
 * @props 参数
 * v-model          false关闭下拉动画
 * pullColors       动画颜色
 * pullWidth        设置宽    
 * pullHeight       设置高
 * offset           滚动条与底部的距离小于多少触发上拉加载
 * scrollHide       是否隐藏滚动条,默认为 false 显示滚动条
 * finished         是否开启上拉加载,默认关闭
 * finishedText     设置上拉加载完后的提示
 * 
 * @refresh         下拉刷新回调函数
 * @load            上拉加载回调函数
 */
export default {
  name: 'pull-refresh',
  props: {                     //api props参数
    pullColors: {              //动画颜色
      type:String,
      default:'#1A73E8'       //默认为蓝色
    },
    pullWidth: {               //宽度
      type:Number,           //默认为100% 
    },
    pullHeight: {           //高度
      type:Number,         //默认为100% 
    }, 
    offset: {             //滚动条与底部的距离小于多少触发上拉加载,默认值为30
      type:Number,
      default:50        //默认为50             
    },
    scrollHide:{              //true 为隐藏滚动条,默认为 false 显示滚动条
      type:Boolean,
      default:false
    },
    finished:{                //是否开启上拉加载,默认关闭
      type:Boolean, 
      default:false
    },
    finishedText:{                    //上拉加载完后的提示
      type:String,             
      default:'到底了,没数据了!'    //默认值
    },
    value: {                   //v-model双向数据绑定
      type:Boolean,            //设置只能为布尔值,false下拉刷新结束
      default:true,           //默认true
      required:true,            //必填
    }
  }, 
  data () {
    return {  
        startY: '',             //保存第一次点击屏幕时的Y坐标
        moveDistance: -35,      //设置动画距离顶部的位置
        moveState: 0,           //开始滑动到结束后状态的变化 0:下拉即可刷新 1:释放即可刷新 2:加载中
        scrollStaus:true,       //添加滚动条到顶部时触发下拉刷新限制,增加用户体验感
        duration: 0,            //动画持续时间(毫秒),0就是没有动画
        scrollTop:0,            //记录滚动条是否在顶部
        scales:1,               //记录刷新完成后缩小隐藏
        rotateCircle:0,         //记录线条圆圈旋转度数
        rotateArrow:false,      //记录下拉显示线条圆圈箭头
        opacity:0.3,            //记录可刷新颜色透明度
        scrollTops:0,           //记录是否有滚动条
        detimes:1000,           //设置防抖时间
        timers:null,            //设置防抖触发
        topButters:0,           //设置顶部缓冲阴影大小 
        bottomButters:0,        //设置地部缓冲阴影大小
        finishend:false,        //设置底部数据完全加载完提示显示
    }
  },
  computed: {
    pullRefresh(){
      return {
        width:this.pullWidth?this.pullWidth+'px':'100%',
        height:this.pullHeight?this.pullHeight+'px':'100%'
      }
    },
    style () {
      return {
        transition: `${this.duration}ms`,
        transform: `translate3d(0,${this.moveDistance}px, 0) scale(${this.scales})`, 
      }
    },
    circleLine(){
      return {
        borderColor:this.pullColors,
        transition: `${this.duration}ms`,                           //改变过渡时间
        transform: `rotate(${100 + this.rotateCircle}deg)`,         //改变旋转角度
        opacity:this.opacity,                                       //改变颜色透明度
      }
    }, 
  },
  methods: {   
    scrolls (e) {  
      this.scrollTops = e.target.scrollTop
      let offsets = e.target.scrollHeight - (e.target.scrollTop + e.target.clientHeight)    //计算滚动条到底部的距离
      let loads = this.offset                      //用户设置滚动条到底部距离多少触发上拉加载方法,默认值为30
      //手写小防抖,防止上拉多次触发上拉加载方法 
      //防抖与节流详情可以自寻百度 
      if(this.finished && this.moveState != 2){                  //判断是否开启上拉加载
        if (this.timers != null) {             
            clearTimeout(this.timers);
        }  
        this.timers = setTimeout(()=>{
            this.timers = null;
            if(offsets <= loads){             //当滚动条小于等于指定距离时触发回调函数
                this.$emit('load') 
            } 
        },500);         //设置200ms触发回调函数
      }
      //可滚动区域大于可视区域才显示上拉加载所有数据加载完提示
      if(e.target.scrollHeight > e.target.clientHeight){
        this.finishend = true
      }
      //阻止滚动条过快从下到上触发下拉刷新
      if(e.target.scrollTop == 0){  
        this.topButters = 80                 //显示缓冲区域 
        setTimeout(()=>{                  //设置滚动条到顶部时500ms后才可以下拉刷新
          this.topButters = 0 
          this.scrollStaus = true
        },700)
      }else{  
        this.scrollStaus = false
      }
      //显示底部缓冲区域
      if(offsets < 1){
        this.bottomButters = 70
        setTimeout(()=>{                  
          this.bottomButters = 0  
        },500)
      }
    },   
    touchStart (e) {
        if(!this.scrollStaus)     return;         //阻止滚动条到顶部时立即触发下拉刷新
        if(this.moveState == 2) return;           //判断是否在更新中 
        this.duration = 0                         // 关闭动画
        this.moveDistance = -35                   // 滑动距离归0 
        this.scales = 1                           // 初始化大小
        this.rotateArrow = false                  // 箭头初始化
        this.startY = e.targetTouches[0].clientY  // 获得开始Y坐标
    },
    touchMove (e) {                             //这里是整个下拉刷新的核心  
        if(!this.scrollStaus)     return;       //阻止滚动条到顶部时立即触发下拉刷新
        if(this.moveState == 2) return;         //判断是否在更新中 
        //判断滚动条是否在顶部,如果不在,则下拉刷新就不能启用。 
        if (this.scrollTops != 0) return;  
        let move = e.targetTouches[0].clientY - this.startY
        //判断手指滑动的距离,只有为正数才代表用户下拉了。
        if (move < 200) {     //限制下拉距离
            //阻止默认事件,在微信浏览器中尤为有用,至于为什么,你去试就知道了。
            // e.preventDefault() 
            //增加滑动阻力的感觉
            this.moveDistance = Math.pow(move, 0.9) - 35      //move的0.9次方
            this.rotateCircle = Math.pow(move, 1.2)           //下拉旋转度数 
            //显示线条圆圈箭头
            if(this.moveDistance > 15) { this.rotateArrow = true}   
            else {this.rotateArrow = false }

            if (this.moveDistance > 40) {
                //如果滑动距离大于50 那我就告诉你,释放即可刷新
                this.opacity = 1
                if (this.moveState === 1) return
                this.moveState = 1
            } else {
                //否则 恢复原样 
                this.opacity = 0.3
                if (this.moveState === 0) return
                this.moveState = 0
            } 
        }
    },
    touchEnd (e) { 
        if(this.moveState == 2) return;         //判断是否在更新中
        // 只要手指拿开,我都需要加上结束时的动画,这里为300ms
        this.duration = 300
        if (this.moveDistance > 50) {
            //这里逻辑跟touchMove一样,但是需要真的加载数据了,那moveState变为2 所以加载动画在这出现
            this.moveState = 2
            //因为还没加载完,我得让加载动画显示着,所以这里移动距离为25
            this.moveDistance = 25 

              //这里就是成功后的回调了
            this.$emit('refresh') 
        } else {
            //否则 给我老老实实恢复原样
            this.moveDistance = -35
        }
    },

  },
  watch: { 
    moveState (state) {
      //监听moveState的状态,0意味着开始也意味着结束,这里是结束,并且只有动画生效我们才能 moveDistance 设为0 
      //手指离开了屏幕,动画生效
      if (state === 0 && this.duration === 300) {
          this.moveDistance = -35
      }
    },
    value: {               //监听v-model值为false则关闭下拉刷新
      handler (val) {  
        if(!val){
          this.scales = 0 
          setTimeout(()=>{ 
            this.moveState = 0  
          },400)
          this.$emit('input',true)      //改变父组件v-model值为true,可以重复下拉刷新
        }
      },
      deep:true       //深度监听
    }, 
  }
}
</script> 

做的不是100%还原,请见谅!()
有问题欢迎讨论!

你可能感兴趣的:(self博客,web,app)