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 相关文章推荐
jQuery EasyUI API 中文文档 DateTimeBox日期时间框
Oct 16 Javascript
javascript单引号和双引号的区别和处理
May 14 Javascript
JS通过ajax动态读取xml文件内容的方法
Mar 24 Javascript
Javascript基础之数组的使用
May 13 Javascript
javascript的函数劫持浅析
Sep 26 Javascript
在js里怎么实现Xcode里的callFuncN方法(详解)
Nov 05 Javascript
详解ECharts使用心得总结
Dec 06 Javascript
jquery实现文本框的禁用和启用
Dec 07 Javascript
javascript基础知识讲解
Jan 11 Javascript
基于js文件加载优化(详解)
Jan 03 Javascript
jquery插件开发模式实例详解
Jul 20 jQuery
vue组件的路由高亮问题解决方法
May 11 Vue.js
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
慎用preg_replace危险的/e修饰符(一句话后门常用)
2013/06/19 PHP
php读取远程gzip压缩网页的方法
2014/12/29 PHP
鼠标经过的文本框textbox变色
2009/05/21 Javascript
jQuery 可以拖动的div实现代码 脚本之家修正版
2009/06/26 Javascript
firefox事件处理之自动查找event的函数(用于onclick=foo())
2010/08/05 Javascript
JavaScript合并两个数组并去除重复项的方法
2015/06/13 Javascript
jQuery手机拨号界面特效代码分享
2015/08/27 Javascript
基于jQuery实现返回顶部实例代码
2016/01/01 Javascript
浅析jquery unbind()方法移除元素绑定的事件
2016/05/24 Javascript
浅析Bootstrap验证控件的使用
2016/06/23 Javascript
Vue数据驱动模拟实现1
2017/01/11 Javascript
javascript中BOM基础知识总结
2017/02/14 Javascript
使用jQuery实现动态添加小广告
2017/07/11 jQuery
详解自定义ajax支持跨域组件封装
2018/02/08 Javascript
vue.js通过路由实现经典的三栏布局实例代码
2018/07/08 Javascript
webpack4 SplitChunks实现代码分隔详解
2019/05/23 Javascript
在vscode 中设置 vue模板内容的方法
2020/09/02 Javascript
一道python走迷宫算法题
2018/01/22 Python
pyqt5自定义信号实例解析
2018/01/31 Python
Python+pandas计算数据相关系数的实例
2018/07/03 Python
Django处理多用户类型的方法介绍
2019/05/18 Python
Django为窗体加上防机器人的验证码功能过程解析
2019/08/14 Python
什么是python的自省
2020/06/21 Python
详解HTML5中的标签
2015/06/19 HTML / CSS
css 如何让背景图片拉伸填充避免重复显示
2013/07/11 HTML / CSS
美国女性卫生用品公司:Thinx
2017/06/30 全球购物
有原因的手表:Flex Watches
2019/03/23 全球购物
BannerBuzz加拿大:在线定制横幅印刷、广告和标志
2020/03/10 全球购物
璀璨的珍珠、密钉和个性化珠宝:Lily & Roo
2021/01/21 全球购物
高中自我鉴定
2013/12/20 职场文书
成考报名单位证明范本
2014/01/16 职场文书
学习走群众路线心得体会
2014/11/05 职场文书
三方协议书
2015/01/27 职场文书
清明扫墓感想
2015/08/11 职场文书
2016秋季小学开学寄语
2015/12/03 职场文书
2016党校学习心得体会范文
2016/01/07 职场文书