基於better-scroll封裝一個上拉載入下拉重新整理元件
1.起因
上拉載入和下拉重新整理在移動端專案中是很常見的需求,遂自己便基於better-scroll封裝了一個下拉重新整理上拉載入元件.
2.過程
better-scroll是目前比較好用的開源滾動庫,提供很多靈活的api供我們開發各種實用的元件,文件地址(https://ustbhuangyi.github.io/better-scroll/doc/zh-hans/#better-scroll),本次主要用到它提供的pullDownRefresh 和 pullUpLoad api 開啟上拉載入和下拉重新整理的功能,同時它還提供兩個event用於傳送請求,pullingUp會在一次上拉載入之後觸發,pullingdown 會在一次下拉重新整理之後觸發,可以在這兩個事件中請求資料.這裡有一個坑就是,每次上拉或者下拉之後需要呼叫finishPullUp或finishPullDown來結束這些動作.另外better-scroll在ios系統上快速滾動可能會出現白屏的bug,而且當你滾動暫停的時候回出現抖動,這些可以通過修改配置項useTransition:false解決,better-scroll會開始以js幀動畫來渲染滑動效果,以下是程式碼部分:
<template> <div class="scroll" ref="wrapper"> <!-- 資料列表 --> <div class="list-wrapper"> <slot name="content"></slot> <!-- 上拉載入 start--> <slot name="pullup"> <div class="pullup-wrapper" v-if="pullUpLoad"> <div class="before-trigger" v-if="!isPullUpLoad"> {{ pullupText }} </div> <div class="after-trigger" v-else> <loading></loading> </div> </div> </slot> <!-- 上拉載入 end--> </div> <!-- 下拉重新整理start --> <slot name="pulldown"> <div class="pulldown-wrapper" :style="pullDownStyle"> <div class="before-trigger" v-if="beforePullDown"> {{ pullDownText }} </div> <div class="after-trigger" v-else> <loading v-if="isPullDown"></loading> <div v-else>重新整理成功</div> </div> </div> </slot> <!-- 下拉重新整理end --> </div> </template> <script> import Bscroll from 'better-scroll'; import loading from '../loading/loading.vue'; export default { componentName : 'scroll', components:{ loading }, data(){ return{ scroll: null, isPullUpLoad : false,// 上拉正在載入中 pullUpDirty: true,// 是否有新資料 isPullDown: false,// 正在重新整理 beforePullDown: true,// 未開始下拉重新整理 pullDownStyle : {} } }, mounted(){ this.initScroll(); }, computed:{ pullupText(){ const moreText = this.pullUpLoad && this.pullUpLoad.more || '上拉載入更多'; const noMoreText = this.pullUpLoad && this.pullUpLoad.nomore || '沒有更多資料了'; return this.pullUpDirty && moreText || noMoreText; }, pullDownText(){ return this.pullDown && this.pullDown.refreshText || '下拉重新整理'; } }, methods:{ forceUpdate(dirty){ if(this.pullUpLoad && this.isPullUpLoad){ this.pullUpDirty = dirty; this.isPullUpLoad = false; this.finishPullUp(); this.refresh(); }else if(this.pullDown && this.isPullDown){ this.isPullDown = false; setTimeout(()=>{// 讓重新整理成功停留一段時間 this.finishPullDown(); this.refresh(); },500) } }, initScroll(){ if(!this.$refs.wrapper){ return } let options = { probeType : this.probeType, click: this.click, startX: this.startX, startY: this.startY, tap: this.tap, pullUpLoad: this.pullUpLoad, pullDownRefresh: this.pullDown && {threshold: 50,stop : 50 } } this.scroll = new Bscroll(this.$refs.wrapper, options); // 代理scrollEnd事件 if(this.scrollEnd){ this.scroll.on('scrollEnd',(pos)=>{ this.$emit('scrollEnd', pos) }) } // 代理scroll事件 if(this.onScroll){ this.scroll.on('scroll',(pos)=>{ console.log(pos.y); let position = pos.y-50 >= 20 && 20 || pos.y-50; this.$set(this.pullDownStyle, 'top', position + 'px') this.$emit('scroll', pos); }) } // 代理pullingUp事件,在一次上拉載入的動作後. if(this.pullUpLoad){ this.onPullingUp(); } // 代理pullingDown事件,在一次下拉重新整理的動作後. if(this.pullUpLoad){ this.onPullingDown(); } }, onPullingUp(){ this.scroll.on('pullingUp',()=>{ this.isPullUpLoad = true; this.$emit('pullingUp'); }) }, onPullingDown(){ this.scroll.on('pullingDown', ()=>{ this.beforePullDown = false; this.isPullDown = true; this.$emit('pullingDown'); }) }, refresh(){ this.scroll && this.scroll.refresh(); }, finishPullUp(){ this.scroll && this.scroll.finishPullUp(); }, finishPullDown(){ this.scroll && this.scroll.finishPullDown(); } }, watch:{ list(){ // 列表有變化重新整理,沒有變化在父元件呼叫forceUpdate this.forceUpdate(true); }, pullDownStyle:{ handler(val){ if(val.top == '-50px'){ this.beforePullDown = true; } }, deep : true } }, props : { list : {// 資料來源 type : Array, default(){ return [] } }, startX : {// 橫軸方向初始化位置 type : Number, default : 0 }, startY : {// 縱軸方向初始化位置 type : Number, default : 0 }, probeType : {// 實時派發滾動位置的等級 type : Number, default : 3 }, click : {// 開啟better-scroll派發的click事件 type : Boolean, default : true, }, tap : { type : Boolean, // 開啟better-scroll派發的tap事件 default : false }, scrollEnd : { // 監聽滾動到結束位置 type : Boolean, default : false }, pullDown : {// 是否開啟下拉重新整理 type : null, default : false }, pullUpLoad : {// 是否開啟上拉載入 type : null, default : false }, onScroll : {// 監聽滾動事件 type : Boolean, default : true } } } </script> <style scopedlang="scss"> .scroll{ width: 100%; height: 100%; overflow: scroll; position: relative; } .list-wrapper{ width: 100%; min-height: 101%; } .pulldown-wrapper{ width: 100%; height: 50px; position: absolute; display: flex; align-items : center; justify-content : center; left: 0; top: -50px; z-index: 100; } .pullup-wrapper{ width: 100%; height: 50px; display: flex; align-items : center; justify-content : center; } </style>