接上篇 https://www.cnblogs.com/chenyingying0/p/12635080.html 上拉載入更多 base/scroll/index.vue <template> <!-- wiper會實例化構造函數,生成swiper實例 --> <!-- ref="swiper"能 ...
接上篇 https://www.cnblogs.com/chenyingying0/p/12635080.html
上拉載入更多
base/scroll/index.vue
<template> <!-- wiper會實例化構造函數,生成swiper實例 --> <!-- ref="swiper"能夠獲取到這個swiper實例 --> <swiper :options="swiperOption" ref='swiper'> <div class="mine-scroll-pull-down" v-if="pullDown"> <!-- ref="pullDownLoading" -- 獲取下拉的loading --> <me-loading :text="pullDownText" inline ref="pullDownLoading" /> </div> <swiper-slide> <slot></slot> </swiper-slide> <div class="mine-scroll-pull-up" v-if="pullUp"> <!-- ref="pullUpLoading" -- 獲取上拉的loading --> <me-loading :text="pullUpText" inline ref="pullUpLoading" /> </div> <div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div> </swiper> </template> <script> // 組件首字母大寫,否則會報錯 import {Swiper,SwiperSlide} from 'vue-awesome-swiper'; import MeLoading from 'base/loading'; import { PULL_DOWN_HEIGHT, PULL_DOWN_TEXT_INIT, PULL_DOWN_TEXT_START, PULL_DOWN_TEXT_ING, PULL_DOWN_TEXT_END, PULL_UP_HEIGHT, PULL_UP_TEXT_INIT, PULL_UP_TEXT_START, PULL_UP_TEXT_ING, PULL_UP_TEXT_END } from './config'; export default { name:"MeScroll", components:{ Swiper, SwiperSlide, MeLoading }, props:{//過濾器 scrollbar:{ type:Boolean, default:true }, data:{//熱門推薦載入完成後傳遞過來的recommends數據 type:[Array,Object] }, pullDown:{//是真就開啟下拉刷新 type:Boolean, default:false }, pullUp:{//是真就開啟下拉載入 type:Boolean, default:false } }, data(){ return { pulling:false,//是否正在下拉 pullDownText:PULL_DOWN_TEXT_INIT,//設置下拉初始化文字 pullUpText:PULL_UP_TEXT_INIT,//設置上拉初始化文字 swiperOption:{ direction:'vertical',//垂直方向 slidesPerView:'auto',//一次顯示幾張 freeMode:true,//任意滑動多少距離 setWrapperSize:true,//根據內容設置容器尺寸 scrollbar:{ el:this.scrollbar?'.swiper-scrollbar':null, hide:true //滾動條自動隱藏 }, on:{ //swiper配置時會觸發sliderMove方法,這裡調用時執行自定義的scroll方法 sliderMove:this.scroll, touchEnd:this.touchEnd//touchEnd是swiper提供的滾動結束的函數,this.touchEnd是我們自己寫的函數 } } } }, methods:{ update(){//不知道怎麼寫就去swiper官網查api //console.log(this.$refs.swiper);//列印swiper實例 this.$refs.swiper && this.$refs.swiper.$swiper.update();//調用swiper.update()更新滾動條 }, scroll(){ const swiper=this.$refs.swiper.$swiper; //如果正在下拉中,不會再次執行 if(this.pulling) return; //console.log(swiper.translate);//列印出滾動條滾過的距離 if(swiper.translate>0){//下拉 if(!this.pullDown){//如果不需要下拉刷新 return; } if(swiper.translate>PULL_DOWN_HEIGHT){ this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_START); }else{ this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_INIT); } }else if(swiper.isEnd){//上拉 if(!this.pullUp){ return; } //是否達到上拉的觸發條件 //swiper的位移加上swiper的高度(617px)-50px的值如果大於當前內容高度 //swiper.translate這個屬性可以獲取到wrapper的位移,其實可以理解為滾動條滾動的距離 //swiper.height這個屬性獲取swiper容器的高度, 也就是顯示區域的高度 //PULL_UP_HEIGHT是我們設置的一個值。為了讓頁面不是到達最低部的時候,可以提前載入內容 //parseInt(swiper.$wrapperEl.css('height'))是wrapper的HTML元素的height屬性, 也就是所有內容的高度 const isPullUp=Math.abs(swiper.translate)+swiper.height-PULL_UP_HEIGHT>parseInt(swiper.$wrapperEl.css('height')); if(isPullUp){//開始上拉 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_START); }else{//保持初始化 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_INIT); } } }, touchEnd(){ const swiper=this.$refs.swiper.$swiper; //如果正在下拉中,不會再次執行 if(this.pulling) return; if(swiper.translate>PULL_DOWN_HEIGHT){//如果距離大於設定的距離 if(!this.pullDown){//如果不需要下拉刷新 return; } this.pulling=true; swiper.allowTouchMove=false;//禁止觸摸 swiper.setTransition(swiper.params.speed);//設置初始速度 swiper.setTranslate(PULL_DOWN_HEIGHT);//移動到設定的位置(拖動過度時回到設置的位置) swiper.params.virtualTranslate=true;//定住不給回彈 this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_ING);//設置正在刷新中的文字 this.$emit("pull-down",this.pullDownEnd);//觸發消息,傳遞結束下拉的函數 }else if(swiper.isEnd){//上拉 //是否達到上拉的觸發條件 const isPullUp=Math.abs(swiper.translate)+swiper.height-PULL_UP_HEIGHT>parseInt(swiper.$wrapperEl.css('height')); if(isPullUp){//開始上拉 if(!this.pullUp){//如果不需要上拉刷新 return; } this.pulling=true; swiper.allowTouchMove=false;//禁止觸摸 swiper.setTransition(swiper.params.speed);//設置初始速度 swiper.setTranslate(-(parseInt(swiper.$wrapperEl.css('height'))+PULL_UP_HEIGHT-swiper.height));//超過拉動距離時回彈 swiper.params.virtualTranslate=true;//定住不給回彈 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_ING);//設置正在刷新中的文字 this.$emit("pull-up",this.pullUpEnd);//觸發消息,傳遞結束下拉的函數 } } }, pullDownEnd(){ const swiper=this.$refs.swiper.$swiper; this.pulling=false; this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_END);//設置載入結束後的文字 swiper.allowTouchMove=true;//可以觸摸 swiper.setTransition(swiper.params.speed);//設置初始速度 swiper.params.virtualTranslate=false;//可以回彈 swiper.setTranslate(0);//移動到最初的位置 }, pullUpEnd(){ const swiper=this.$refs.swiper.$swiper; this.pulling=false; this.$refs.pullUpLoading.setText(PULL_UP_TEXT_END);//設置載入結束後的文字 swiper.allowTouchMove=true;//可以觸摸 swiper.params.virtualTranslate=false;//可以回彈 } }, watch:{//檢測數據變化的事件 data(){ this.update();//data數據變化時執行update函數 } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .swiper-container{ width:100%; height:100%; overflow:hidden; & .swiper-slide{ height:auto; } } //預設是不顯示的 .mine-scroll-pull-down{ position:absolute; left:0; bottom:100%; width:100%; height:80px; } .mine-scroll-pull-up{ position:absolute; left:0; top:100%; width:100%; height:30px; } </style>
base/scroll/config.js
// 下拉相關常量 export const PULL_DOWN_HEIGHT=100; export const PULL_DOWN_TEXT_INIT='再拉,再拉就刷新給你看'; export const PULL_DOWN_TEXT_START='夠了啦,鬆開人家嘛'; export const PULL_DOWN_TEXT_ING='刷的好累呀,喵~'; export const PULL_DOWN_TEXT_END='刷新完啦'; // 上拉相關常量 export const PULL_UP_HEIGHT=50; export const PULL_UP_TEXT_INIT='上拉初始文本'; export const PULL_UP_TEXT_START='上拉開始文本'; export const PULL_UP_TEXT_ING='正在上拉中'; export const PULL_UP_TEXT_END='上拉結束文本';
pages/home/index.vue
<template> <div class="home"> <header class="g-header-container"> <!-- 沒有內容自閉合即可--> <home-header/> </header> <!-- 滾動條接收到數據後開始更新 --> <!-- pullDown是布爾值,可以使用簡寫直接傳入,不加冒號 --> <!-- 接收到pull-down消息後,觸發pullToRefresh方法 --> <me-scroll :data="recommends" pullDown pullUp @pull-down="pullToRefresh" @pull-up="pullToLoadMore"> <home-slider ref="slider" /> <home-nav /> <!-- 接收熱門推薦載入完畢的消息 --> <home-recommend @loaded="getRecommends" ref="recommend" /> </me-scroll> <div class="g-backup-container"></div> <!-- 當前頁面存在二級頁面時需要使用router-view --> <router-view></router-view> </div> </template> <script> import MeScroll from 'base/scroll'; import HomeHeader from './header'; import HomeSlider from './slider'; import HomeNav from './nav'; import HomeRecommend from './recommend'; export default { name:"Home", components:{ HomeHeader, HomeSlider, MeScroll, HomeNav, HomeRecommend }, data(){ return{ recommends:[] } }, methods:{ getRecommends(recommends){ this.recommends=recommends; }, updateScroll(){ }, pullToRefresh(end){ this.$refs.slider.update().then(end); }, pullToLoadMore(end){ this.$refs.recommend.update().then(end).catch(err=>{ //沒有更多內容時 if(err){ console.log(err); } end(); //禁止繼續載入更多數據 //替換上拉時的loading,改為“沒有更多數據了” }); } } } </script> <style lang="scss" scoped> // 引入前面需要加波浪線,否則會報錯 @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
pages/home/recommend.vue
<template> <div class="recommend"> <h3 class="recommend-title">熱賣推薦</h3> <div class="loading-container" v-if="!recommends.length"> <!-- 完整寫法是 inline:inline ,不過布爾值類型可以直接寫 inline --> <me-loading inline /> </div> <ul class="recommend-list"> <li class="recommend-item" v-for="(item,index) in recommends" :key="index"> <router-link class="recommend-link" :to="{name:'home-product',params:{id:item.baseinfo.itemId}}"> <p class="recommend-pic"><img class="recommend-img" v-lazy="item.baseinfo.picUrl" alt=""></p> <!-- <p class="recommend-pic"><img class="recommend-img" :src="item.baseinfo.picUrl" alt=""></p> --> <p class="recommend-name">{{item.name.shortName}}</p> <p class="recommend-oriPrice"><del>¥{{item.price.origPrice}}</del></p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">{{item.price.actPrice}}</strong></span> <span class="recommend-count">{{item.remind.soldCount}}件已售</span> </p> </router-link> </li> </ul> </div> </template> <script> import {getHomeRecommend} from 'api/home'; import MeLoading from 'base/loading'; export default { name:"HomeRecommend", data(){ return { recommends:[], curPage:1, totalPage:1 } }, components:{ MeLoading }, created(){ this.getRecommends(); }, methods:{ //api update(){ return this.getRecommends();//返回promise對象 }, getRecommends(){ if(this.curPage>this.totalPage) return Promise.reject(new Error('沒有更多了')); return getHomeRecommend(this.curPage).then(data=>{ return new Promise(resolve=>{ if(data){ //console.log(data); this.curPage++; this.totalPage=data.totalPage; // concat合併數組內容,每次獲取的數據都追加進來 this.recommends=this.recommends.concat(data.itemList); this.$emit("loaded",this.recommends);//熱門推薦區域載入完畢後觸發消息 resolve(); } }) }); } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .recommend{ position:relative; width:100%; padding:10px 0; font-size:$font-size-l; text-align:center; &:before, &:after{ content:""; display:block; position:absolute; top:50%; width:40%; height:1px; background:#ddd; } &:before{ left:0; } &:after{ right:0; } } .recommend-list{ @include flex-between(); flex-wrap:wrap; } .recommend-title{ margin-bottom:8px; } .recommend-item{ width:49%; background:#fff; box-shadow:0 1px 1px 0 rgba(0,0,0,0.12); margin-bottom:8px; } .recommend-link{ display:block; } .recommend-pic{ position:relative; width:100%; padding-top:100%;// 可以實現高度與寬度一致 margin-bottom:5px; } .recommend-img{ width:100%; position:absolute; top:0; left:0; height:100%; } .recommend-name{ height:40px; padding:0 5px; margin-bottom:8px; line-height:1.5; @include multiline-ellipsis(); text-align:left; } .recommend-oriPrice{ padding:0 5px; margin-bottom:8px; color:#ccc; del{ } } .recommend-info{ @include flex-between(); padding:0 5px; margin-bottom:8px; } .recommend-price{ color:#e61414; &-num{ font-size:20px; } } .recommend-count{ color:#999; } .loading-container{ padding-top:150px; } </style>
效果圖
完善滾動條組件
base/scroll/index.vue
<template> <!-- wiper會實例化構造函數,生成swiper實例 --> <!-- ref="swiper"能夠獲取到這個swiper實例 --> <swiper :options="swiperOption" ref='swiper'> <div class="mine-scroll-pull-down" v-if="pullDown"> <!-- ref="pullDownLoading" -- 獲取下拉的loading --> <me-loading :text="pullDownText" inline ref="pullDownLoading" /> </div> <swiper-slide> <slot></slot> </swiper-slide> <div class="mine-scroll-pull-up" v-if="pullUp"> <!-- ref="pullUpLoading" -- 獲取上拉的loading --> <me-loading :text="pullUpText" inline ref="pullUpLoading" /> </div> <div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div> </swiper> </template> <script> // 組件首字母大寫,否則會報錯 import {Swiper,SwiperSlide} from 'vue-awesome-swiper'; import MeLoading from 'base/loading'; import { PULL_DOWN_HEIGHT, PULL_DOWN_TEXT_INIT, PULL_DOWN_TEXT_START, PULL_DOWN_TEXT_ING, PULL_DOWN_TEXT_END, PULL_UP_HEIGHT, PULL_UP_TEXT_INIT, PULL_UP_TEXT_START, PULL_UP_TEXT_ING, PULL_UP_TEXT_END } from './config'; export default { name:"MeScroll", components:{ Swiper, SwiperSlide, MeLoading }, props:{//過濾器 scrollbar:{ type:Boolean, default:true }, data:{//熱門推薦載入完成後傳遞過來的recommends數據 type:[Array,Object] }, pullDown:{//是真就開啟下拉刷新 type:Boolean, default:false }, pullUp:{//是真就開啟下拉載入 type:Boolean, default:false } }, methods:{ update(){//不知道怎麼寫就去swiper官網查api //console.log(this.$refs.swiper);//列印swiper實例 this.$refs.swiper && this.$refs.swiper.$swiper.update();//調用swiper.update()更新滾動條 }, scrollToTop(speed,runCallback){ // slideTo回到第x張幻燈片,swiper的API提供的 this.$refs.swiper && this.$refs.swiper.$swiper.slideTo(0,speed,runCallback); }, init(){ //將不需要設置getter和setter的數據,放在init中初始化即可 this.pulling=false;//是否正在下拉 this.pullDownText=PULL_DOWN_TEXT_INIT;//設置下拉初始化文字 this.pullUpText=PULL_UP_TEXT_INIT;//設置上拉初始化文字 this.swiperOption={ direction:'vertical',//垂直方向 slidesPerView:'auto',//一次顯示幾張 freeMode:true,//任意滑動多少距離 setWrapperSize:true,//根據內容設置容器尺寸 scrollbar:{ el:this.scrollbar?'.swiper-scrollbar':null, hide:true //滾動條自動隱藏 }, on:{ //swiper配置時會觸發sliderMove方法,這裡調用時執行自定義的scroll方法 sliderMove:this.scroll, touchEnd:this.touchEnd,//touchEnd是swiper提供的滾動結束的函數,this.touchEnd是我們自己寫的函數, transitionEnd:this.scrollEnd } }; }, scroll(){ const swiper=this.$refs.swiper.$swiper; //滾動時觸發scroll事件 this.$emit("scroll",swiper.translate,this.$refs.swiper.$swiper); //如果正在下拉中,不會再次執行 if(this.pulling) return; //console.log(swiper.translate);//列印出滾動條滾過的距離 if(swiper.translate>0){//下拉 if(!this.pullDown){//如果不需要下拉刷新 return; } if(swiper.translate>PULL_DOWN_HEIGHT){ this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_START); }else{ this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_INIT); } }else if(swiper.isEnd){//上拉 if(!this.pullUp){ return; } //是否達到上拉的觸發條件 //swiper的位移加上swiper的高度(617px)-50px的值如果大於當前內容高度 //swiper.translate這個屬性可以獲取到wrapper的位移,其實可以理解為滾動條滾動的距離 //swiper.height這個屬性獲取swiper容器的高度, 也就是顯示區域的高度 //PULL_UP_HEIGHT是我們設置的一個值。為了讓頁面不是到達最低部的時候,可以提前載入內容 //parseInt(swiper.$wrapperEl.css('height'))是wrapper的HTML元素的height屬性, 也就是所有內容的高度 const isPullUp=Math.abs(swiper.translate)+swiper.height-PULL_UP_HEIGHT>parseInt(swiper.$wrapperEl.css('height')); if(isPullUp){//開始上拉 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_START); }else{//保持初始化 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_INIT); } } }, scrollEnd(){ this.$emit("scroll-end",this.$refs.swiper.$swiper.translate,this.$refs.swiper.$swiper); }, touchEnd(){ const swiper=this.$refs.swiper.$swiper; //如果正在下拉中,不會再次執行 if(this.pulling) return; if(swiper.translate>PULL_DOWN_HEIGHT){//如果距離大於設定的距離 if(!this.pullDown){//如果不需要下拉刷新 return; } this.pulling=true; swiper.allowTouchMove=false;//禁止觸摸 swiper.setTransition(swiper.params.speed);//設置初始速度 swiper.setTranslate(PULL_DOWN_HEIGHT);//移動到設定的位置(拖動過度時回到設置的位置) swiper.params.virtualTranslate=true;//定住不給回彈 this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_ING);//設置正在刷新中的文字 this.$emit("pull-down",this.pullDownEnd);//觸發消息,傳遞結束下拉的函數 }else if(swiper.isEnd){//上拉 //是否達到上拉的觸發條件 const isPullUp=Math.abs(swiper.translate)+swiper.height-PULL_UP_HEIGHT>parseInt(swiper.$wrapperEl.css('height')); if(isPullUp){//開始上拉 if(!this.pullUp){//如果不需要上拉刷新 return; } this.pulling=true; swiper.allowTouchMove=false;//禁止觸摸 swiper.setTransition(swiper.params.speed);//設置初始速度 swiper.setTranslate(-(parseInt(swiper.$wrapperEl.css('height'))+PULL_UP_HEIGHT-swiper.height));//超過拉動距離時回彈 swiper.params.virtualTranslate=true;//定住不給回彈 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_ING);//設置正在刷新中的文字 this.$emit("pull-up",this.pullUpEnd);//觸發消息,傳遞結束下拉的函數 } } }, pullDownEnd(){ const swiper=this.$refs.swiper.$swiper; this.pulling=false; this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_END);//設置載入結束後的文字 swiper.allowTouchMove=true;//可以觸摸 swiper.setTransition(swiper.params.speed);//設置初始速度 swiper.params.virtualTranslate=false;//可以回彈 swiper.setTranslate(0);//移動到最初的位置 //下拉完成後,顯示head組件(下拉過程中會被隱藏) setTimeout(()=>{ this.$emit("pull-down-transition-end"); },swiper.params.speed); }, pullUpEnd(){ const swiper=this.$refs.swiper.$swiper; this.pulling=false; this.$refs.pullUpLoading.setText(PULL_UP_TEXT_END);//設置載入結束後的文字 swiper.allowTouchMove=true;//可以觸摸 swiper.params.virtualTranslate=false;//可以回彈 } }, watch:{//檢測數據變化的事件 data(){ this.update();//data數據變化時執行update函數 } }, created(){//在created中初始化數據 this.init(); } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .swiper-container{ width:100%; height:100%; overflow:hidden; & .swiper-slide{ height:auto; } } //預設是不顯示的 .mine-scroll-pull-down{ position:absolute; left:0; bottom:100%; width:100%; height:80px; } .mine-scroll-pull-up{ position:absolute; left:0; top:100%; width:100%; height:30px; } </style>