vue.js移动端app之上拉加载以及下拉刷新实战


Posted in Javascript onSeptember 11, 2017

上拉加载以及下拉刷新都是移动端很常见的功能,在搜索或者一些分类列表页面常常会用到。

vue.js移动端app之上拉加载以及下拉刷新实战

跟横向滚动一样,我们还是采用better-scroll这个库来实现。由于better已经更新了新的版本,之前是0.几的版本,更新了一下发现,现在已经是1.2.6这个版本了,新版本多了些 比较好用的api,所以我也重写了之前的代码,用新的api来实现上拉加载以及下拉刷新。

首先把基本的样式写好,这里就略过了,然后引入better-scroll库

import BScroll from 'better-scroll'

其次,在mounted生命周期实例化scroll,可以获取完数据后再new,也可以先new后,获取完数据调用refresh。

实例时需要传入一个配置参数,由于参数比较多,具体的请参考文档,这里只讲2个重点的:

//是否开启下拉刷新,可传入true或者false,如果需要更多配置可以传入一个对象

pullDownRefresh:{
  threshold:80,
  stop:40
}

//是否开启上拉加载,同上,上拉无stop参数,这里需要注意是负数
pullUpLoad:{
  threshold:-80,
}

/**
 * 
 * @param threshold 触发事件的阀值,即滑动多少距离触发
 * @param stop 下拉刷新后回滚距离顶部的距离(为了给loading留出一点空间)
 */

以上的数字个人感觉比较合适,但是这里有一个问题,由于我采用的是淘宝flexible.js来适配,这就导致:在安卓下80这个距离是合适的,但是到了iphone6s下,由于被缩放了3陪,所以现在80在iphone6s下就是27左右了。

所以,对于不同缩放程度的屏幕,还需要乘以对应的缩放比。

淘宝flexible.js里面其实已经有这个获取屏幕缩放比方法,这里直接从里面拿:

//在util.js里面加一个方法
export function getDeviceRatio(){
  var isAndroid = window.navigator.appVersion.match(/android/gi);
  var isIPhone = window.navigator.appVersion.match(/iphone/gi);
  var devicePixelRatio = window.devicePixelRatio;
  var dpr;
  if (isIPhone) {
    // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
    if (devicePixelRatio >= 3) {        
      dpr = 3;
    } else if (devicePixelRatio >= 2){
      dpr = 2;
    } else {
      dpr = 1;
    }
  } else {
    // 其他设备下,仍旧使用1倍的方案
    dpr = 1;
  }
  return dpr
}
import{ DEVICE_RATIO} from '../base/js/api.js'
/*获取当前缩放比*/
const DEVICE_RATIO=getDeviceRatio();


 /*下拉配置*/
const DOWN_CONFIG={
 threshold:80*DEVICE_RATIO,
 stop:40*DEVICE_RATIO
}
/*上拉配置*/
const UP_CONFIG={
 threshold:-80*DEVICE_RATIO,
}

this.scroller = new BScroll(scrollWrap,{
 click:true,
 probeType:3,
 pullDownRefresh:DOWN_CONFIG,
 pullUpLoad:UP_CONFIG
});

实例化后,接下来就是监听上拉和下拉事件了。betterScroll新增了一些事件,主要的有:

/*下拉事件*/
this.scroller.on('pullingDown',()=> {});

/*上拉事件*/
this.scroller.on('pullingUp',()=>{});

触发上拉或者下拉事件后,需要我们调用 this.scroller.finishPullDown() 或者 this.scroller.finishPullUp() 来通知better-scroll事件完成。

大致的流程是这样的:

this.scroller.on('pullingDown',()=> {
  
  <!-- 1. 发送请求获取数据 -->
  
  <!-- 2. 获取成功后,通知事件完成 -->
  
  <!-- 3. 修改data数据,在nextTick调用refresh -->
});

通常操作完成后都需要我们手动触发refresh方法来重新计算可滚动的距离,因此可以写一个watch监听数据的变化,这样我们只需要改变数据,不用每次操作数据后都调用refresh方法。

watch:{
 dataList(){
  this.$nextTick(()=>{
   this.scroller.refresh(); 
  }) 
 }
},

如果你使用的版本还是旧的,那可以在on( scroll )事件的时候进行判断来实现功能

this.scroller.on("scroll",(pos)=>{ 
  //获取整个滚动列表的高度
  var height=getStyle(scroller,"height");

  //获取滚动外层wrap的高度
  var pageHeight=getStyle(scrollWrap,"height");

  //触发事件需要的阀值
  var distance=80*DEVICE_RATIO;

  //参数pos为当前位置

  if(pos.y>distance){ 

    //console.log("下拉");
    //do something
   
  }else if(pos.y-pageHeight<-height-distance){

    //console.log("上拉");
    //do something
  }

为了防止多次触发,需要加2个开关类的东西;

var onPullUp=true;

var onPullDown=true;

每次触发事件时,?⒍杂Φ目?厣柚梦?alse, 等操作完成后,再重新设置为true,否则多次下拉或者上拉就会触发多次事件。通过设置开关可以保证每次只有一个事件在进行。

最后,来封装成一个组件

<template>
  <div ref="wrapper" class="list-wrapper"> 
    <div class="scroll-content">    
      <slot></slot>     
    </div>   
  </div>
</template>

由于每个页面需要滚动的具体内容都是不一样的,所以用了一个插槽来分发。

组件需要的参数由父级传入,通过prop来接收并设置默认值

export default {
  props: {
   dataList:{
    type: Array,
    default: []
   },
   probeType: {
    type: Number,
    default: 3
   },
   click: {
    type: Boolean,
    default: true
   },  
   pullDownRefresh: {
    type: null,
    default: false
   },
   pullUpLoad: {
    type: null,
    default: false
   },  
  }

组件挂载后,在事件触发时并不直接处理事件,而是向父级发送一个事件,父级通过在模板v-on接收事件并处理后续的逻辑

mounted() {
  this.scroll = new BScroll(this.$refs.wrapper, {
      probeType: this.probeType,
      click: this.click,    
      pullDownRefresh: this.pullDownRefresh,
      pullUpLoad: this.pullUpLoad,
    })

  this.scroll.on('pullingUp',()=> {
    if(this.continuePullUp){
      this.beforePullUp();
      this.$emit("onPullUp","当前状态:上拉加载");
    }
  });

  this.scroll.on('pullingDown',()=> {
    this.beforePullDown();
    this.$emit("onPullDown","当前状态:下拉加载更多");
  }); 
}

父组件在使用时,需要传入配置参数Props以及处理子组件发射的事件,并且用具体的内容并替换掉 slot 标签

<Scroller 
    id="scroll"
    ref="scroll" 
    :dataList="filmList"
    :pullDownRefresh="DOWN_CONFIG"
    :pullUpLoad="UP_CONFIG"
    @onPullUp="pullUpHandle"
    @onPullDown="pullDownHandle"
   >

    <ul>
       <router-link class="film-list" v-for="(v,i) in filmList" :key="v.id" tag="li" :to='{path:"/film-detail/"+v.id}'>
          <div class="film-list__img">
             <img v-lazy="v.images.small" alt="" />        
          </div>
          <div class="film-list__detail">
            <p class="film-list__detail__title">{{v.title}}</p>
            <p class="film-list__detail__director">导演:{{filterDirectors(v.directors)}}</p>
            <p class="film-list__detail__year">年份:{{v.year}}<span>{{v.stock}}</span></p>
            <p class="film-list__detail__type">类别:{{v.genres.join(" / ")}}<span></span></p>
            <p class="film-list__detail__rank">评分:<span>{{v.rating.average}}分</span></p>
          </div>             
        </router-link>
     </ul>     
   </Scroller>

父组件可以通过this.$refs.xxx来获取到子组件,可以调用子组件里面的方法;

computed:{
    scrollElement(){
      return this.$refs.scroll
    }
  }

完整的scroller组件内容如下

<template>
     <div ref="wrapper" class="list-wrapper"> 
       <div class="scroll-content">    
         <slot></slot>
         <div>
           <PullingWord v-show="!inPullUp&&dataList.length>0" :loadingWord="beforePullUpWord"></PullingWord>
           <Loading v-show="inPullUp" :loadingWord='PullingUpWord'></Loading>
         </div>    
       </div> 

       <transition name="pullDown">
         <Loading class="pullDown" v-show="inPullDown" :loadingWord='PullingDownWord'></Loading>
       </transition> 
     </div>
   </template>


   <script >
    import BScroll from 'better-scroll'
    import Loading from './loading.vue'
    import PullingWord from './pulling-word'

    const PullingUpWord="正在拼命加载中...";
    const beforePullUpWord="上拉加载更多";
    const finishPullUpWord="加载完成";

    const PullingDownWord="加载中...";

    export default {
     props: {
      dataList:{
       type: Array,
       default: []
      },
      probeType: {
       type: Number,
       default: 3
      },
      click: {
       type: Boolean,
       default: true
      },  
      pullDownRefresh: {
       type: null,
       default: false
      },
      pullUpLoad: {
       type: null,
       default: false
      },  
     },
     data() {
       return { 
         scroll:null,
         inPullUp:false,
         inPullDown:false,
         beforePullUpWord,
         PullingUpWord,
         PullingDownWord,
         continuePullUp:true
       }
     },
      
     mounted() {
       setTimeout(()=>{
         this.initScroll();

         this.scroll.on('pullingUp',()=> {
           if(this.continuePullUp){
             this.beforePullUp();
             this.$emit("onPullUp","当前状态:上拉加载");
           }
         });

         this.scroll.on('pullingDown',()=> {
           this.beforePullDown();
           this.$emit("onPullDown","当前状态:下拉加载更多");
         });

       },20)
       
     },
     methods: {
       initScroll() {
         if (!this.$refs.wrapper) {
           return
         }
         this.scroll = new BScroll(this.$refs.wrapper, {
           probeType: this.probeType,
           click: this.click,    
           pullDownRefresh: this.pullDownRefresh,
           pullUpLoad: this.pullUpLoad,
         })
       },
       beforePullUp(){
         this.PullingUpWord=PullingUpWord;
         this.inPullUp=true;
       }, 
       beforePullDown(){
         this.disable();
         this.inPullDown=true;
       },
       finish(type){
         this["finish"+type]();
         this.enable();
         this["in"+type]=false; 
       },
       disable() {
         this.scroll && this.scroll.disable()
       },
       enable() {
         this.scroll && this.scroll.enable()
       },
       refresh() {
         this.scroll && this.scroll.refresh()
       }, 
       finishPullDown(){
         this.scroll&&this.scroll.finishPullDown()
       },
       finishPullUp(){
         this.scroll&&this.scroll.finishPullUp()
       },   
     },
        
     watch: {
       dataList() {        
         this.$nextTick(()=>{
           this.refresh();            
         }) 
       }
     },
     components: {
       Loading,
       PullingWord
     }
    }

   </script>

具体内容可以查看github , 项目地址如下:https://github.com/linrunzheng/vueApp

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
关于图片验证码设计的思考
Jan 29 Javascript
js word表格动态添加代码
Jun 07 Javascript
jquery 页面滚动到底部自动加载插件集合
Jan 31 Javascript
javascript递归回溯法解八皇后问题
Apr 22 Javascript
Bootstrap精简教程中秋大放送
Sep 15 Javascript
jQuery图片切换动画效果
Feb 28 Javascript
jquery实现一个全局计时器(商城可用)
Jun 30 jQuery
Scala解析Json字符串的实例详解
Oct 11 Javascript
详解Vue+axios+Node+express实现文件上传(用户头像上传)
Aug 10 Javascript
vuejs中监听窗口关闭和窗口刷新事件的方法
Sep 21 Javascript
React手稿之 React-Saga的详解
Nov 12 Javascript
详解TypeScript中的类型保护
Apr 29 Javascript
Chrome调试折腾记之JS断点调试技巧
Sep 11 #Javascript
JS中的Replace()传入函数时的用法详解
Sep 11 #Javascript
浅谈vue+webpack项目调试方法步骤
Sep 11 #Javascript
JS实现快速比较两个字符串中包含有相同数字的方法
Sep 11 #Javascript
node.js 利用流实现读写同步,边读边写的方法
Sep 11 #Javascript
checkbox:click事件触发span元素内容改变的方法
Sep 11 #Javascript
Node.js 使用流实现读写同步边读边写功能
Sep 11 #Javascript
You might like
PHP自动生成后台导航网址的最佳方法
2013/08/27 PHP
使用tp框架和SQL语句查询数据表中的某字段包含某值
2019/10/18 PHP
JavaScript 加号(+)运算符号
2009/12/06 Javascript
关于firefox的ElementTraversal 接口 使用说明
2010/11/11 Javascript
通过Jscript中@cc_on 语句识别IE浏览器及版本的代码
2011/05/07 Javascript
读jQuery之六 缓存数据功能介绍
2011/06/21 Javascript
jqPlot 图表中文API使用文档及源码和在线示例
2012/02/07 Javascript
基于javascipt-dom编程 table对象的使用
2013/04/22 Javascript
获取下拉列表框的值是数组,split,$.inArray示例
2013/11/13 Javascript
JS实现点击按钮后框架内载入不同网页的方法
2015/05/05 Javascript
详解JavaScript中Date.UTC()方法的使用
2015/06/12 Javascript
浅析AngularJS中的指令
2016/03/20 Javascript
js实现图片淡入淡出切换简易效果
2016/08/22 Javascript
jQuery组件easyui对话框实现代码
2016/08/25 Javascript
JS+html5 canvas实现的简单绘制折线图效果示例
2017/03/13 Javascript
JavaScript数组去重的几种方法
2019/04/07 Javascript
JS计算斐波拉切代码实例
2019/09/12 Javascript
layui 富文本图片上传接口与普通按钮 文件上传接口的例子
2019/09/23 Javascript
JavaScript实现网页下拉菜单效果
2020/11/20 Javascript
[02:31]2014DOTA2国际邀请赛2009专访:干爹表现出乎意料 看好DK杀回决赛
2014/07/20 DOTA
python中列表元素连接方法join用法实例
2015/04/07 Python
Python实现对比不同字体中的同一字符的显示效果
2015/04/23 Python
远程部署工具Fabric详解(支持Python3)
2019/07/04 Python
Python3.7黑帽编程之病毒篇(基础篇)
2020/02/04 Python
pycharm新建Vue项目的方法步骤(图文)
2020/03/04 Python
基于Python的一个自动录入表格的小程序
2020/08/05 Python
英国泰坦旅游网站:全球陪同游览,邮轮和铁路旅行
2016/11/29 全球购物
彪马美国官网:PUMA美国
2017/03/09 全球购物
大学生个人推荐信范文
2013/11/25 职场文书
劳动之星获奖感言
2014/02/01 职场文书
产品销售计划书
2014/05/04 职场文书
2014年房地产销售工作总结
2014/12/01 职场文书
本科毕业论文指导教师评语
2014/12/30 职场文书
关于感谢信的范文
2015/01/23 职场文书
python实现黄金分割法的示例代码
2021/04/28 Python
CSS 鼠标点击拖拽效果的实现代码
2022/12/24 HTML / CSS