您好,登錄后才能下訂單哦!
使用vue怎么實現移動端下拉刷新和上拉加載?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
1、下拉刷新
DropDownRefresh.vue
<template lang="html"> <div class="refreshMoudle" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" :> <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)" :> <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" : ></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" : ></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" : ></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" : ></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" : ></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>
關于使用vue怎么實現移動端下拉刷新和上拉加載問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。