首页 > 编程 > JavaScript > 正文

vue移动端下拉刷新和上拉加载的实现代码

2019-11-19 12:59:15
字体:
来源:转载
供稿:网友

由于自身的项目比较简单,只有几个H5页面,用来嵌入app中,所有没有引入移动端的UI框架,但是介于能让用户在浏览H5页面时有下拉刷新和上拉加载,有更好的用户体验,自己写组件实现。

1、下拉刷新DropDownRefresh.vue

<template lang="html">  <div class="refreshMoudle" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" :style="{transform: 'translate3d(0,' + top + 'px, 0)'}">   <header class="pull-refresh">    <slot name="pull-refresh">     <div class="down-tip" v-if="dropDownState==1">      <img v-if="dropDownStateText.downImg" class="down-tip-img" :src="require('../../assets/images/refreshAndReload/'+dropDownStateText.downImg)">      <span class="down-tip-text">{{dropDownStateText.downTxt}}</span>     </div>     <div class="up-tip" v-if="dropDownState==2">      <img v-if="dropDownStateText.upImg" class="up-tip-img" :src="require('../../assets/images/refreshAndReload/'+dropDownStateText.upImg)">      <span class="up-tip-text">{{dropDownStateText.upTxt}}</span>     </div>     <div class="refresh-tip" v-if="dropDownState==3">      <img v-if="dropDownStateText.refreshImg" class="refresh-tip-img" :src="require('../../assets/images/refreshAndReload/'+dropDownStateText.refreshImg)">      <span class="refresh-tip-text">{{dropDownStateText.refreshTxt}}</span>     </div>    </slot>   </header>   <slot></slot>  </div></template><script>export default { props: {  onRefresh: {   type: Function,   required: false  } }, data () {  return {   defaultOffset: 100, // 默认高度, 相应的修改.releshMoudle的margin-top和.down-tip, .up-tip, .refresh-tip的height   top: 0,   scrollIsToTop: 0,   startY: 0,   isDropDown: false, // 是否下拉   isRefreshing: false, // 是否正在刷新   dropDownState: 1, // 显示1:下拉刷新, 2:松开刷新, 3:刷新中……   dropDownStateText: {    downTxt: '下拉刷新',    downImg: '',    upTxt: '松开刷新',    upImg: 'release.png',    refreshTxt: '刷新中...',    refreshImg: 'refresh.gif'   }  } }, created () {  if (document.querySelector('.down-tip')) {   // 获取不同手机的物理像素(dpr),以便适配rem   this.defaultOffset = document.querySelector('.down-tip').clientHeight || this.defaultOffset  } }, methods: {  touchStart (e) {   this.startY = e.targetTouches[0].pageY  },  touchMove (e) {   this.scrollIsToTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop // safari 获取scrollTop用window.pageYOffset   if (e.targetTouches[0].pageY > this.startY) { // 下拉    this.isDropDown = true    if (this.scrollIsToTop === 0 && !this.isRefreshing) {     // 拉动的距离     let diff = e.targetTouches[0].pageY - this.startY - this.scrollIsToTop     this.top = Math.pow(diff, 0.8) + (this.dropDownState === 3 ? this.defaultOffset : 0)     if (this.top >= this.defaultOffset) {      this.dropDownState = 2      e.preventDefault()     } else {      this.dropDownState = 1      e.preventDefault()     }    }   } else {    this.isDropDown = false    this.dropDownState = 1   }  },  touchEnd (e) {   if (this.isDropDown && !this.isRefreshing) {    if (this.top >= this.defaultOffset) { // do refresh     this.refresh()     this.isRefreshing = true     console.log(`do refresh`)    } else { // cancel refresh     this.isRefreshing = false     this.isDropDown = false     this.dropDownState = 1     this.top = 0    }   }  },  refresh () {   this.dropDownState = 3   this.top = this.defaultOffset   setTimeout(() => {    this.onRefresh(this.refreshDone)   }, 1200)  },  refreshDone () {   this.isRefreshing = false   this.isDropDown = false   this.dropDownState = 1   this.top = 0  } }}</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped>.refreshMoudle { width: 100%; margin-top: -100px; -webkit-overflow-scrolling: touch; /* ios5+ */}.pull-refresh { width: 100%; color: #999; transition-duration: 200ms;}.refreshMoudle .down-tip,.up-tip,.refresh-tip { display: flex; align-items: center; justify-content: center; height: 100px;}.refreshMoudle .down-tip-img,.up-tip-img,.refresh-tip-img { width: 35px; height: 35px; margin-right: 5px;}</style>
2、上拉加载PullUpReload.vue
<template lang="html"> <div class="loadMoudle" @touchstart="touchStart($event)" @touchmove="touchMove($event)" :style="{transform: 'translate3d(0,' + top + 'px, 0)'}">  <slot></slot>  <footer class="load-more">   <slot name="load-more">    <div class="moreData-tip" v-if="pullUpState==1">     <span class="moreData-tip-text">{{pullUpStateText.moreDataTxt}}</span>    </div>    <div class="loadingMoreData-tip" v-if="pullUpState==2">     <span class="icon-loading"></span>     <span class="loadingMoreData-tip-text">{{pullUpStateText.loadingMoreDataTxt}}</span>    </div>    <div class="noMoreData-tip" v-if="pullUpState==3">     <span class="connectingLine"></span>     <span class="noMoreData-tip-text">{{pullUpStateText.noMoreDataTxt}}</span>     <span class="connectingLine"></span>    </div>   </slot>  </footer> </div></template><script>export default { props: {  parentPullUpState: {   default: 0  },  onInfiniteLoad: {   type: Function,   require: false  } }, data () {  return {   top: 0,   startY: 0,   pullUpState: 0, // 1:上拉加载更多, 2:加载中……, 3:我是有底线的   isLoading: false, // 是否正在加载   pullUpStateText: {    moreDataTxt: '上拉加载更多',    loadingMoreDataTxt: '加载中...',    noMoreDataTxt: '我是有底线的'   }  } }, methods: {  touchStart (e) {   this.startY = e.targetTouches[0].pageY  },  touchMove (e) {   if (e.targetTouches[0].pageY < this.startY) { // 上拉    this.judgeScrollBarToTheEnd()   }  },  // 判断滚动条是否到底  judgeScrollBarToTheEnd () {   let innerHeight = document.querySelector('.loadMoudle').clientHeight   // 变量scrollTop是滚动条滚动时,距离顶部的距离   let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop   // 变量scrollHeight是滚动条的总高度   let scrollHeight = document.documentElement.clientHeight || document.body.scrollHeight   // 滚动条到底部的条件   if (scrollTop + scrollHeight >= innerHeight) {    if (this.pullUpState !== 3 && !this.isLoading) {     this.pullUpState = 1     this.infiniteLoad()     // setTimeout(() => {     //  this.infiniteLoad()     // }, 200)    }   }  },  infiniteLoad () {   this.pullUpState = 2   this.isLoading = true   setTimeout(() => {    this.onInfiniteLoad(this.infiniteLoadDone)   }, 800)  },  infiniteLoadDone () {   this.pullUpState = 0   this.isLoading = false  } }, watch: {  parentPullUpState (curVal, oldVal) {   this.pullUpState = curVal  } }}</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped>.load-more { width: 100%; color: #c0c0c0; background: #f7f7f7;}.moreData-tip,.loadingMoreData-tip,.noMoreData-tip { display: flex; align-items: center; justify-content: center; height: 150px;}.loadMoudle .icon-loading { display: inline-flex; width: 35px; height: 35px; background: url(../../assets/images/refreshAndReload/loading.png) no-repeat; background-size: cover; margin-right: 5px; animation: rotating 2s linear infinite;}@keyframes rotating { 0% {  transform: rotate(0deg); } 100% {  transform: rotate(1turn); }}.connectingLine { display: inline-flex; width: 150px; height: 2px; background: #ddd; margin-left: 20px; margin-right: 20px;}</style>
3、对两个组件的使用
<template> <section class="container">  <v-refresh :on-refresh="onRefresh">  <v-reload :on-infinite-load="onInfiniteLoad" :parent-pull-up-state="infiniteLoadData.pullUpState">  <div class="bank_lists">   <div class="bank_box">    <div class="bank_list" v-for="item in bank_list" :key="item.id">     <div class="bank_icon" :style="{ 'background': 'url(' + require('../assets/images/56_56/'+item.iconName) + ') no-repeat', 'background-size': '100%' }" ></div>     <span class="bank_name">{{item.bankName}}</span>    </div>   </div>  </div>  <div class="hot_box">   <div class="hot_header">    <span class="hot_name">热门推荐</span>    <div class="more_box">     <span class="more_text">查看更多</span>     <span class="more_icon"></span>    </div>   </div>   <div class="hot_centenrt">    <div class="hot_centent_left">     <span class="hot_left_name">{{hot_centent_left.name}}</span>     <span class="hot_left_desc">{{hot_centent_left.desc}}</span>     <div class="hot_left_img" :style="{ 'background': 'url(' + require('../assets/images/bank/'+hot_centent_left.imgName) + ') no-repeat', 'background-size': '100%' }" ></div>    </div>    <div class="hot_centent_right">     <div class="hot_right_top">      <div class="hot_right_text_box">       <span class="hot_right_name">{{hot_c_r_one.name}}</span>       <span class="hot_right_desc">{{hot_c_r_one.desc}}</span>      </div>      <div class="hot_right_img" :style="{ 'background': 'url(' + require('../assets/images/bank/'+hot_c_r_one.imgName) + ') no-repeat', 'background-size': '100%' }" ></div>     </div>     <div class="hot_right_bottom">      <div class="hot_right_text_box2">       <span class="hot_right_name2">{{hot_c_r_two.name}}</span>       <span class="hot_right_desc2">{{hot_c_r_two.desc}}</span>      </div>      <div class="hot_right_img" :style="{ 'background': 'url(' + require('../assets/images/bank/'+hot_c_r_two.imgName) + ') no-repeat', 'background-size': '100%' }" ></div>     </div>    </div>   </div>  </div>  <div class="card_state">   <div class="card_progress border-right">    <div class="progress_icon"></div>    <div class="card_text">     <span class="card_state_name">{{card_progress.name}}</span>     <span class="card_desc">{{card_progress.desc}}</span>    </div>   </div>   <div class="card_activation">    <div class="activation_icon"></div>    <div class="card_text">     <span class="card_state_name">{{card_activation.name}}</span>     <span class="card_desc">{{card_activation.desc}}</span>    </div>   </div>  </div>  <div class="card_order">   <div class="border_bottom card_content_bottom">    <div class="hot_header">     <span class="hot_name">热卡排行</span>    </div>   </div>   <div slot="load-more">   <li class="card_list" v-for="(item,index) in infiniteLoadData.pullUpList" :key="item.id">    <div class="card_content" :class="infiniteLoadData.pullUpList.length - 1 != index? 'card_content_bottom':''">     <div class="card_img" :style="{ 'background': 'url(' + require('../assets/images/bank/'+item.imgName) + ') no-repeat', 'background-size': '100%' }" ></div>     <div class="card_list_text">      <p class="card_name">{{item.cardName}}</p>      <p class="card_title">{{item.cardTitle}}</p>      <div class="card_words_lists">       <div class="card_words bor_rad_20">        <p class="card_word">{{item.cardWordOne}}</p>       </div>       <div v-if="item.cardWordTwo" class="card_words card_words_two bor_rad_20">        <p class="card_word">{{item.cardWordTwo}}</p>       </div>      </div>     </div>    </div>   </li>   </div>  </div>  </v-reload>  </v-refresh> </section></template><script>import DropDownRefresh from './common/DropDownRefresh'import PullUpReload from './common/PullUpReload'export default { data () {  return {   bank_list: [    {     iconName: 'zhaoshang.png',     bankName: '招商银行'    },    {     iconName: 'minsheng.png',     bankName: '民生银行'    },    {     iconName: 'pingancar.png',     bankName: '平安联名'    },    {     iconName: 'xingye.png',     bankName: '兴业银行'    },    {     iconName: 'shanghai.png',     bankName: '上海银行'    },    {     iconName: 'jiaotong.png',     bankName: '交通银行'    },    {     iconName: 'guangda.png',     bankName: '光大银行'    },    {     iconName: 'more.png',     bankName: '全部银行'    }   ],   hot_centent_left: {    bankName: '交通银行',    name: '交行Y-POWER黑卡',    desc: '额度100%取现',    imgName: 'jiaohangY-POWER.png'   },   hot_c_r_one: {    bankName: '招商银行',    name: '招行YOUNG卡',    desc: '生日月双倍积分',    imgName: 'zhaohangYOUNG.png'   },   hot_c_r_two: {    bankName: '光大银行',    name: '光大淘票票公仔联名卡',    desc: '电影达人必备',    imgName: 'guangdalianming.png'   },   card_progress: {    name: '办卡进度',    desc: '让等待随处可见'   },   card_activation: {    name: '办卡激活',    desc: '让等待随处可见'   },   card_list: [    {     bankName: '平安联名',     imgName: 'pinganqiche.png',     cardName: '平安银行信用卡',     cardTitle: '平安银行汽车之家联名单币卡',     cardWordOne: '首年免年费',     cardWordTwo: '加油88折'    },    {     bankName: '上海银行',     imgName: 'shanghaitaobao.png',     cardName: '上海银行信用卡',     cardTitle: '淘宝金卡',     cardWordOne: '积分抵现',     cardWordTwo: '首刷有礼'    },    {     bankName: '华夏银行',     imgName: 'huaxiaiqiyi.png',     cardName: '华夏银行信用卡',     cardTitle: '华夏爱奇艺悦看卡',     cardWordOne: '送爱奇艺会员',     cardWordTwo: '商城8折'    },    {     bankName: '浦发银行',     imgName: 'pufajianyue.png',     cardName: '浦发银行信用卡',     cardTitle: '浦发银行简约白金卡',     cardWordOne: '团购立减',     cardWordTwo: '酒店优惠 免年费'    },    {     bankName: '中信银行',     imgName: 'zhongxinbaijin.png',     cardName: '中信银行信用卡',     cardTitle: '中信银行i白金信用卡',     cardWordOne: '首刷有礼',     cardWordTwo: '双倍积分'    }   ],   // 上拉加载的设置   infiniteLoadData: {    initialShowNum: 3, // 初始显示多少条    everyLoadingNum: 3, // 每次加载的个数    pullUpState: 0, // 子组件的pullUpState状态    pullUpList: [], // 上拉加载更多数据的数组    showPullUpListLength: this.initialShowNum // 上拉加载后所展示的个数   }  } }, mounted () {  this.getStartPullUpState()  this.getPullUpDefData() }, methods: {  // 获取上拉加载的初始数据  getPullUpDefData () {   this.infiniteLoadData.pullUpList = []   for (let i = 0; i < this.infiniteLoadData.initialShowNum; i++) {    this.infiniteLoadData.pullUpList.push(this.card_list[i])   }  },  getStartPullUpState () {   if (this.card_list.length === this.infiniteLoadData.initialShowNum) {    // 修改子组件的pullUpState状态    this.infiniteLoadData.pullUpState = 3   } else {    this.infiniteLoadData.pullUpState = 0   }  },  // 上拉一次加载更多的数据  getPullUpMoreData () {   this.showPullUpListLength = this.infiniteLoadData.pullUpList.length   if (this.infiniteLoadData.pullUpList.length + this.infiniteLoadData.everyLoadingNum > this.card_list.length) {    for (let i = 0; i < this.card_list.length - this.showPullUpListLength; i++) {     this.infiniteLoadData.pullUpList.push(this.card_list[i + this.showPullUpListLength])    }   } else {    for (let i = 0; i < this.infiniteLoadData.everyLoadingNum; i++) {     this.infiniteLoadData.pullUpList.push(this.card_list[i + this.showPullUpListLength])    }   }   if (this.card_list.length === this.infiniteLoadData.pullUpList.length) {    this.infiniteLoadData.pullUpState = 3   } else {    this.infiniteLoadData.pullUpState = 0   }  },  // 下拉刷新  onRefresh (done) {   // 如果下拉刷新和上拉加载同时使用,下拉时初始化上拉的数据   this.getStartPullUpState()   this.getPullUpDefData()   done() // call done  },  // 上拉加载  onInfiniteLoad (done) {   if (this.infiniteLoadData.pullUpState === 0) {    this.getPullUpMoreData()   }   done()  } }, components: {  'v-refresh': DropDownRefresh,  'v-reload': PullUpReload }}</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped>@import "../assets/css/not2rem.css";.container { display: flex; flex-direction: column; width: 750px; height: 1334px; background-color: #f7f7f7;}.bank_lists { width: 100%; height: 320px; margin-top: 0px; background-color: #fff;}.bank_box { display: flex; flex-wrap: wrap; padding: 2px 7px 42px 7px;}.bank_list { width: 100px; height: 98px; margin: 40px 42px 0 42px;}.bank_icon { width: 56px; height: 56px; margin: 0 22px 18px;}.bank_name { display: inline-flex; width: 110px; height: 24px; line-height: 24px; font-size: 24px; color: #333;}.hot_box { width: 100%; height: 420px; margin-top: 10px; background: #fff;}.hot_header { display: flex; justify-content: space-between; align-items: center; width: 674px; height: 80px; margin: 0 30px 0 46px;}.hot_name { display: inline-flex; height: 28px; line-height: 28px; font-size: 28px; color: #333;}.more_text { display: inline-flex; height: 24px; line-height: 24px; font-size: 24px; color: #999;}.more_icon { display: inline-flex; margin-left: 20px; width: 11px; height: 20px; background: url("../assets/images/icon/more.png") no-repeat; background-size: 100%;}.hot_centenrt { display: flex; flex-direction: row; width: 710px; height: 320px; margin: 0 20px 20px 20px;}.hot_centent_left { flex-direction: column; width: 350px; height: 320px; background: #f7f7f7;}.hot_left_name { display: inline-flex; width: 282px; height: 24px; margin: 50px 34px 0 34px; font-size: 24px; line-height: 24px; color: #333;}.hot_left_desc { display: inline-flex; width: 282px; height: 20px; margin: 12px 34px 0 34px; font-size: 20px; line-height: 20px; color: #999;}.hot_left_img { width: 220px; height: 142px; margin-left: 34px; margin-top: 34px;}.hot_centent_right { flex-direction: column; width: 350px; height: 320px; margin-left: 10px;}.hot_right_top { display: flex; flex-direction: row; width: 100%; height: 156px; background: #f7f7f7;}.hot_right_text_box { display: flex; flex-direction: column; width: 180px; height: 58px; margin: 49px 20px 0 20px;}.hot_right_name { display: inline-flex; width: 100%; height: 24px; line-height: 24px; font-size: 24px; color: #333;}.hot_right_desc { display: inline-flex; margin-top: 10px; width: 100%; height: 24px; line-height: 24px; font-size: 24px; color: #999;}.hot_right_img { width: 110px; height: 70px; margin-top: 43px;}.hot_right_bottom { display: flex; flex-wrap: wrap; width: 100%; height: 156px; margin-top: 8px; background: #f7f7f7;}.hot_right_text_box2 { display: flex; flex-direction: column; width: 180px; margin: 31px 20px 0 20px;}.hot_right_name2 { display: inline-flex; width: 100%; height: 58px; line-height: 30px; font-size: 24px; color: #333;}.hot_right_desc2 { display: inline-flex; margin-top: 12px; width: 100%; height: 24px; line-height: 24px; font-size: 24px; color: #999;}.card_state { display: flex; flex-direction: row; width: 100%; height: 128px; margin-top: 10px; background-color: #fff;}.card_progress { display: inline-flex; width: 327px; height: 88px; margin: 20px 0 20px 48px;}.progress_icon { width: 48px; height: 48px; margin: 20px 0; background: url("../assets/images/icon/search.png") no-repeat; background-size: 100%;}.activation_icon { width: 48px; height: 48px; margin: 20px 0; background: url("../assets/images/icon/activation.png") no-repeat; background-size: 100%;}.card_text { width: 228px; height: 66px; margin: 11px 20px 11px 30px;}.card_state_name { display: inline-flex; width: 100%; height: 28px; line-height: 28px; font-size: 28px; color: #333;}.card_desc { display: inline-flex; width: 100%; height: 22px; line-height: 22px; font-size: 22px; margin-top: 16px; color: #999;}.card_activation { display: inline-flex; width: 326px; height: 88px; margin: 20px 0 20px 48px;}.card_order { width: 100%; height: auto; margin-top: 10px; background-color: #fff;}.border_bottom { width: 100%; height: 80px;}.card_list { width: 100%; height: 228px; list-style-type: none;}.card_content { display: flex; flex-direction: row; width: 700px; height: 228px; margin-left: 50px;}.card_img { width: 186px; height: 120px; margin: 54px 0 54px 20px;}.card_list_text { flex-direction: column; width: 386px; height: 124px; margin: 52px 34px 52px 74px;}.card_name { width: 100%; height: 28px; line-height: 28px; font-size: 28px; color: #333;}.card_title { width: 100%; height: 24px; margin-top: 20px; line-height: 24px; font-size: 24px; color: #666;}.card_words_lists { display: flex; flex-direction: row;}.card_words { height: 36px; margin-top: 16px; background-color: #e8ca88;}.card_word { height: 20px; padding: 8px 18px; line-height: 20px; font-size: 20px; color: #4b4b4b;}.card_words_two { margin-left: 20px;}</style>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表