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 scoped lang="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>
