某一個(gè)頁(yè)面的列表使用better-scroll的上拉加載和下拉刷新功能,better-scroll被封裝成vue的組件
1。頁(yè)面中
<my-scroll
ref="myScroll"
:startY="parseInt(startY)"
:listArr="state.testList" // 列表數(shù)據(jù)
:haveDataFlag="state.listHaveDataFlag" //分頁(yè)請(qǐng)求,下一頁(yè)是否有數(shù)據(jù)的標(biāo)識(shí)
:pullup="pullupFlag" // 是否開(kāi)啟上拉加載
@scrollEnd="onLoadFun" // 上拉加載的方法
:pullDownRefresh="pullDownRefreshObj" // 是否派發(fā)下拉刷新
@pullingDown="onPullingDown" //下拉刷新的方法
>
<test-list></test-list> //列表
</my-scroll>
data() {
return {
pullDownRefresh: true,
startY: 0,
pullDownRefreshThreshold: 90,
pullDownRefreshStop: 40,
pullupFlag: true, // 上拉加載
};
},
computed:{
pullDownRefreshObj() {
return this.pullDownRefresh
? {
threshold: parseInt(this.pullDownRefreshThreshold),
stop: parseInt(this.pullDownRefreshStop)
}
: false;
}
}
methods:{
// 上拉加載
async onLoadFun() {
await this.updateInfo({
pageNum: this.state.pageNum+ 1
});
if (this.state.nextTestList.length === 0) {
// 如果下一頁(yè)沒(méi)有數(shù)據(jù),停止上拉加載
this.pullupFlag = false;
return;
}
// 請(qǐng)求接口
await this.listRequest();
},
// 下拉刷新
async onPullingDown() {
//打印this可以看到_isDestroyed,當(dāng)前實(shí)例是否已經(jīng)被銷毀
if (this._isDestroyed) return;
await this.updateInfo({
stockMarketPageNum: 1, // 行情列表分頁(yè)
stockMarketList: [], // 行情列表
nextStockMarketList: [] //行情新請(qǐng)求列表
});
this.listRequest();
},
// 重新初始化組件
rebuildScroll() {
Vue.nextTick(() => {
this.$refs.myScroll.destroy();
this.$refs.myScroll.initScroll();
});
},
},
watch: {
startY(nval,oval) {
this.rebuildScroll();
}
},
- 組件中
<template>
<div class="my-scroll" ref="myScroll">
<div class="scroll-content">
<slot></slot>
<div class="pull-up-box" v-if="fatherNeedPullFlag">
<img
v-show="isPullUp"
src="@/assets/img/loading_01.gif"
alt
style="width:0.2rem;height0.2rem;"
/>
<div :class="listArr.length > 0 ? 'have-data':'no-data'">
<span v-show="haveDataFlag">沒(méi)有更多數(shù)據(jù)了</span>
</div>
</div>
</div>
<slot
name="pulldown"
:pullDownRefresh="pullDownRefresh"
:pullDownStyle="pullDownStyle"
:beforePullDown="beforePullDown"
:isPullingDown="isPullingDown"
>
<div ref="pulldown" class="pulldown-wrapper" :style="pullDownStyle" v-if="pullDownRefresh">
<div class="before-trigger" v-if="beforePullDown"></div>
<div class="after-trigger" v-else>
<div v-if="isPullingDown" class="loading">
<img style="width:0.3rem;height:0.3rem;" src="@/assets/img/loading_01.gif" />
</div>
<div v-else>
<p class="refresh-txt">{{ refreshTxt }}</p>
</div>
</div>
</div>
</slot>
</div>
</template>
<script type="text/ecmascript-6">
import BScroll from "better-scroll";
export default {
name: "myScroll",
data() {
return {
beforePullDown: true,
pullDownStyle: "",
isPullingDown: false,
isRebounding: false
};
},
props: {
scrollFun: {
type: Function,
default: function() {
return f => f;
}
},
/**
* 1 滾動(dòng)的時(shí)候會(huì)派發(fā)scroll事件,會(huì)截流。
* 2 滾動(dòng)的時(shí)候?qū)崟r(shí)派發(fā)scroll事件,不會(huì)截流。
* 3 除了實(shí)時(shí)派發(fā)scroll事件,在慣性或動(dòng)畫的情況下仍然能實(shí)時(shí)派發(fā)scroll事件
*/
probeType: {
type: Number,
default: 1
},
/**
* 點(diǎn)擊列表是否派發(fā)click事件
*/
click: {
type: Boolean,
default: true
},
/**
* 是否開(kāi)啟橫向滾動(dòng)
*/
scrollX: {
type: Boolean,
default: false
},
/**
* 是否派發(fā)滾動(dòng)事件
*/
listenScroll: {
type: Boolean,
default: false
},
/**
* 列表的數(shù)據(jù)
*/
listArr: {
type: Array,
default: null
},
/**
* 是否派發(fā)滾動(dòng)到底部的事件,用于上拉加載
*/
pullup: {
type: Boolean,
default: false
},
/**
* 是否派發(fā)頂部下拉的事件,用于下拉刷新
*/
pullDownRefresh: {
type: null,
default: false
},
/**
* 是否派發(fā)列表滾動(dòng)開(kāi)始的事件
*/
beforeScroll: {
type: Boolean,
default: false
},
/**
* 當(dāng)數(shù)據(jù)更新后,刷新scroll的延時(shí)。
*/
refreshDelay: {
type: Number,
default: 20
},
bounce: {
// 當(dāng)滾動(dòng)超過(guò)邊緣的時(shí)候會(huì)有一小段回彈動(dòng)畫
type: Boolean,
default: true
},
fatherNeedPullFlag: {
// 是否展示底部加載動(dòng)畫和文字
type: Boolean,
default: true
},
// nextListArr: { // 下次請(qǐng)求的新數(shù)據(jù)
// type:Array,
// default:[]
// },
isPullUp: {
type: Boolean,
default: false
},
haveDataFlag: {
type: Boolean,
default: false
}
},
created() {
this.pullDownInitTop = -40;
},
computed: {
refreshTxt() {
return (this.pullDownRefresh && this.pullDownRefresh.txt) || "刷新成功";
}
},
mounted() {
this.$nextTick(() => {
this._initScroll();
});
},
destroyed() {
this.$refs.myScroll && this.$refs.myScroll.destroy();
},
methods: {
_initScroll() {
if (!this.$refs.myScroll) {
return;
}
let options = {
probeType: this.probeType,
scrollX: this.scrollX,
bounce: this.bounce, // 回彈動(dòng)畫
pullup: this.pullup,
pullDownRefresh: this.pullDownRefresh,
taps: true,
click: true
};
// better-scroll的初始化
this.scroll = new BScroll(this.$refs.myScroll, options);
// 是否派發(fā)滾動(dòng)事件
if (this.listenScroll) {
this.scroll.on("scroll", pos => {
this.$emit("scroll", pos);
});
}
// 是否派發(fā)滾動(dòng)到底部事件,用于上拉加載
if (this.pullup) {
this.scroll.on("scrollEnd", () => {
// 滾動(dòng)到底部
if (this.scroll.y <= this.scroll.maxScrollY + 50) {
this.$emit("scrollEnd");
}
});
}
// 監(jiān)聽(tīng)滾動(dòng)高度
if (this.scrollFun) {
this.scroll.on("scroll", scrollValue => {
// 滾動(dòng)到底部
// console.log(scrollValue.y)
this.$emit("scrollFun", scrollValue);
});
}
// 是否派發(fā)頂部下拉事件,用于下拉刷新
if (this.pullDownRefresh) {
this._initPullDownRefresh();
}
// 是否派發(fā)列表滾動(dòng)開(kāi)始的事件
if (this.beforeScroll) {
this.scroll.on("beforeScrollStart", () => {
this.$emit("beforeScroll");
});
}
},
forceUpdate() {
if (this.pullDownRefresh && this.isPullingDown) {
this.isPullingDown = false;
this._reboundPullDown().then(() => {
this._afterPullDown();
});
}
if (this.pullup && this.isPullUp) {
this.isPullUp = false;
this.scroll.finishPullUp();
this.refresh();
} else {
this.refresh();
}
},
_afterPullDown() {
setTimeout(() => {
this.pullDownStyle = `top:${this.pullDownInitTop}px`;
this.beforePullDown = true;
this.isRebounding = false;
this.refresh();
}, this.scroll.options.bounceTime);
},
_initPullDownRefresh() {
this.scroll.on("pullingDown", () => {
this.beforePullDown = false;
this.isPullingDown = true;
this.$emit("pullingDown");
});
this.scroll.on("scroll", pos => {
if (!this.pullDownRefresh) {
return;
}
if (this.beforePullDown) {
this.pullDownStyle = `top:${Math.min(
pos.y + this.pullDownInitTop,
0
)}px`;
}
if (this.isRebounding) {
this.pullDownStyle = `top:${0 -
(this.pullDownRefresh.stop - pos.y)}px`;
}
});
},
_reboundPullDown() {
const { stopTime = 300 } = this.pullDownRefresh;
return new Promise(resolve => {
setTimeout(() => {
this.isRebounding = true;
this.scroll.finishPullDown();
resolve();
}, stopTime);
});
},
disable() {
// 代理better-scroll的disable方法
this.scroll && this.scroll.disable();
},
enable() {
// 代理better-scroll的enable方法
this.scroll && this.scroll.enable();
},
refresh() {
// 代理better-scroll的refresh方法
this.scroll && this.scroll.refresh();
},
scrollTo() {
// 代理better-scroll的scrollTo方法
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments);
},
scrollToElement() {
// 代理better-scroll的scrollToElement方法
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments);
},
destroy() {
this.scroll.destroy();
}
},
watch: {
// 監(jiān)聽(tīng)數(shù)據(jù)的變化,延時(shí)refreshDelay時(shí)間后調(diào)用refresh方法重新計(jì)算,保證滾動(dòng)效果正常,如果是下拉刷新,時(shí)間稍微調(diào)整長(zhǎng)點(diǎn)
listArr(nVal, oVal) {
let times = 800;
if (this.isPullingDown) {
times = 800;
} else {
times = 30;
}
setTimeout(() => {
this.refresh();
this.forceUpdate();
}, times);
}
}
};
</script>
<style scoped lang='less'>
.no-data {
margin-top: 2.4rem;
}
.have-data {
margin-top: 0.2rem;
}
.my-scroll {
overflow: hidden;
height: 100%;
position: relative;
}
.pulldown-wrapper {
height: 0.5rem;
position: absolute;
top: -9px;
left: 0;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
transition: all;
}
.scroll-content {
position: relative;
z-index: 2000;
}
.pull-up-box {
width: 100%;
height: 0.2rem;
// margin: 0.2rem 0 0.2rem 0;
color: #999;
font-size: 14px;
}
.refresh-txt {
height: 0.3rem;
width: 1rem;
border-radius: 3px;
background-color: black;
opacity: 0.7;
color: #fff;
z-index: 2999;
position: fixed;
top: 2rem;
left: 50%;
transform: translate(-50%, 0);
line-height: 0.3rem;
}
</style>