vue-scroll-load-top-bottom
vue组件
支持向上和向下滚动加载
使用方法
npm install vue-scroll-load-top-bottom
代码样例 App.vue
<template>
<div class="wrapper">
<div ref="pageContainer" class="pr l_0 t_0 w_p100 h_p100 dfx flex_direction_column">
<div class="dfx pr w_p100 minh_400" v-show="showSection1">
<span>内容1 <button @click="hideSection1">隐藏此区块</button></span>
</div>
<div id="scrollerWrapper" ref="scrollerWrapper" class="flex1 w_p100 pr ovh ovy_scroll">
<vue-scroll-load-top-bottom
ref="scroller"
id="scroller"
class=""
:wrapper="$refs.scrollerWrapper"
:load-bottom-disabled="loadBottomDisabled"
:load-top-disabled="loadTopDisabled"
@on-load-top="onLoadTop"
@on-load-bottom="onLoadBottom">
<div class="dfx pr w_p100 ta_c line_height_2 bs_border transition_height ovh" slot="loadTop" :class="{ h_70: isLoadingTopData, h_0: !isLoadingTopData }">
<p>加载中</p>
</div>
<div class="pr">
<div v-for="(item, index) in list" :key="index" class="item">Page: {{ item.page }} Index: {{ index }}</div>
</div>
<div class="dfx w_p100 ta_c line_height_2 bs_border transition_height ovh" slot="loadBottom" :class="{ h_70: isLoadingBottomData, h_0: !isLoadingBottomData }">
<p>加载中···</p>
</div>
</vue-scroll-load-top-bottom>
</div>
<div class="dfx pr w_p100 minh_400" v-show="showSection2">
<span>内容2 <button @click="hideSection2">隐藏此区块</button></span>
</div>
</div>
</div>
</template>
<script>
import vueScrollLoadTopBottom from 'vue-scroll-load-top-bottom';
export default {
data() {
return {
init: false,
list: [],
prevPageNo: 3,
pageNo: 4,
nextPageNo: 5,
pageContainer: null,
pageInited: false,
loadTopDisabled: true,
loadBottomDisabled: false,
isLoadingTopData: false,
isLoadingBottomData: false,
showSection1: true,
showSection2: false,
};
},
components: {
vueScrollLoadTopBottom,
},
mounted() {
this.pageContainer = this.$refs.pageContainer;
this.scroller = this.$refs.scroller;
this.pageInited = true;
},
methods: {
hideSection1() {
this.showSection1 = false;
this.$nextTick(() => {
this.scroller.checkInitContentIsEnough();
});
},
hideSection2() {
this.showSection2 = false;
this.$nextTick(() => {
this.scroller.checkInitContentIsEnough();
});
},
makeList() {
const arr = [];
for (let i = 0; i < 2; i += 1) {
// eslint-disable-next-line
arr.push({ page: this.pageNo });
}
return arr;
},
onLoadBottom() {
console.log('onLoadBottom 执行了');
if (this.nextPageNo > 10) {
this.loadBottomDisabled = true;
this.showSection2 = true;
return false;
}
this.isLoadingBottomData = true;
setTimeout(() => {
this.pageNo = this.init ? this.nextPageNo : this.pageNo;
this.list = this.list.concat(this.makeList());
if (this.init) {
this.nextPageNo += 1;
}
this.$refs.scroller.releaseBottomLock();
this.isLoadingBottomData = false;
this.init = true;
}, 15e2);
return true;
},
onLoadTop() {
console.log('onLoadTop 执行了');
if (this.prevPageNo < 1) {
this.loadTopDisabled = true;
this.showSection1 = true;
this.pageContainer.scrollTop = this.scroller.heightSubtract +
Math.abs(this.scroller.bcr.top);
this.$refs.scroller.releaseTopLock();
return false;
}
this.isLoadingTopData = true;
setTimeout(() => {
this.pageNo = this.prevPageNo;
this.list = this.makeList().concat(this.list);
this.prevPageNo -= 1;
this.isLoadingTopData = false;
this.$refs.scroller.releaseTopLock();
this.$nextTick(() => {
/* eslint-disbale no-mixed-operators */
this.$refs.scrollerWrapper.scrollTop = this.scroller.heightSubtract +
Math.abs(this.scroller.bcr.top) - this.scroller.wrapperOffsetTop;
});
}, 15e2);
return true;
},
},
};
</script>
<style scoped>
.pr {
position: relative;
}
.pa {
position: absolute;
}
.l_0 {
left: 0;
}
.t_0 {
top: 0;
}
.dfx {
display: flex;
align-items: center;
justify-content: center;
}
.flex1 {
flex: 1 0;
}
.flex_direction_column {
flex-direction: column;
}
.minh_400 {
min-height: 10rem;
}
.h_0 {
height: 0;
}
.h_60 {
height: 1.5rem;
}
.h_70 {
height: 1.75rem;
}
.bc_red {
background-color: red;
}
.bc_blue {
background-color: blue;
}
.ta_c {
text-align: center;
}
.line_height_2 {
line-height: 2;
}
.w_p100 {
width: 100%;
}
.h_p100 {
height: 100%;
}
.ovh {
overflow: hidden;
}
.ovy_scroll {
overflow-y: scroll;
}
.item {
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
.item:nth-of-type(2n) {
background-color: red;
height: 5rem;
}
.item:nth-of-type(2n+1) {
background-color: blue;
height: 9rem;
}
.transition_height {
transition: height ease-in-out;
}
.transition_duration_300 {
transition-duration: 300ms;
}
/* */
.wrapper {
position: fixed;
left: 50%;
top: 0;
transform: translate3d(-50%, 0, 0);
height: 100%;
width: 400px;
background-color: #f3f3f3;
}
button {
display: inline-block;
appearance: none;
border: 1px solid #ddd;
padding: 3px 10px;
border-radius: 4px;
}
</style>
props:
propValue | required | default | description |
---|
wrapper | false | null | 组件父级overflow-y:scroll的dom元素。当父级容器不是为全屏并且设置了高度时会用到 |
delay | false | 200(ms) | 检测组件状态的时间间隔 |
loadTopDisabled | false | true | 禁止向上加载。当 loadTopDisabled 和 loadBottomDisabled 都为false时会取消监听 |
loadBottomDisabled | false | false | 禁止向下加载。当 loadTopDisabled 和 loadBottomDisabled 都为false时会取消监听 |
topDistance | false | 100 | 向上加载触发距离 |
bottomDistance | false | 200 | 向下加载触发距离 |
touchThreshold | false | 40 | 触摸阈值 |
loadAuto | false | true | 当内容不够时是否自动触发loadBottom |
events:
eventName | params | description |
---|
on-content-is-not-enough | | 内容不够时触发 |
on-load-top | | 向上加载时触发。会锁定向上加载状态。需手动releaseTopLock() |
on-load-bottom | | 向下加载时触发。会锁定向下加载状态。需手动releaseBottomLock() |
other events:
eventName | params | description |
---|
releaseTopLock | | 释放向上加载的锁 |
releaseBottomLock | | 释放向下加载的锁 |
checkInitContentIsEnough | | 手动触发判断内容是否够填充容器 |