需求
当滑动到底部的时候,通知父组件可以加载更多。
原理
scrollTop
:滚动视窗的高度距离window顶部的距离,它会随着往上滚动而不断增加,初始值是0,它是一个变化的值;
clientHeight
:它是一个定值,表示屏幕可是区域的高度;
scrollHeight
:页面不能滚动时是不存在的,body长度超过window时才会出现,所表示body所有元素的长度,
由上面的三个值所产生一个原理公式:
scrollTop + clientHeight >= scrollHeight
这会就是滚动到了底部了。
实现
布局
<template>
<div class="list" ref="feed">
<slot></slot>
<div class="list__bottom">{
{
statusText}}</div>
</div>
</template>
获得当前的滚动元素,并绑定srcoll
事件
mounted(){
this.scrollEl = getScroller(this.$el as HTMLElement);
on(this.scrollEl, 'scroll', this.handleScroll);
this.$once('hook:beforeDestory', () => {
off(this.scrollEl, 'scroll', this.handleScroll);
})
}
实现滚动监听
获得srcollTop
、clientHeight
和scrollerHeight
,计算他们的差值,判断是否触底;当触底的时候即调用父组件的加载更多函数,此时isLoadMore
为true
,不可以再加载,只有false
才可以更多。当父组件告诉你已经不能加载更多isFinished=true
时也不可以再加载更多。
private handleScroll(){
if(this.isLoadMore || this.isFinished) return;
// 滚动条距离顶部的距离
const srcollTop = getScrollTop(this.scrollEl);
// 可视区域高度
const clientHeight = getVisibleHeight(this.scrollEl);
// 内容的总高度
const scrollerHeight = getVisibleHeight(this.placeholder);
if(srcollTop + clientHeight >= scrollerHeight - (+this.offset)) {
this.$emit('loadMore');
}
}
更新底部的加载提示
get statusText(){
if(this.isFinished) return this.finishedText;
else if(this.isLoadMore) return this.loadingText;
return ''
}
修改触底的判断逻辑
我以为按照上面的逻辑就OK了,然而我进行测试之后发现了这个逻辑的不合理性。当你在一个页面中这么使用加载更多
组件是:
<template>
<div class="wrapper">
<div class="tmp"></div>
<ar-list :isLoadMore="isLoadMore" :isFinished="isFinished" @loadMore="initData">
<ul>
<li class="list-item" v-for="(item, index) in list" :key="index">{
{
item}}</li>
</ul>
</ar-list>
</div>
</template>
tmp
是一个有200px
高度的div
,此时获得的scrollTop
包含了200px
,同理,当加载更多组件
上方含有占有一定高度的元素时,必然会影响到scrllTop
的取值,于是我们需要换一个思路。
先分别获得容器和插槽的getBoundingClientRect
,然后计算他们的bottom
差是否小于offset
值,如果是则触底。这里是依据两个底部值来做计算的。
先在插槽外部包裹一个ref=placeholder
的div
:
<template>
<div class="ar-list" ref="feed">
<div class="slot-wrapper" ref="placeholder">
<slot></slot>
</div>
<div class="ar-list__bottom">{
{
statusText}}</div>
</div>
</template>
修改触底逻辑:
private handleScroll(){
if(this.isLoadMore || this.isFinished) return;
let scrollerRect;
if (this.scrollEl.getBoundingClientRect) {
scrollerRect = this.scrollEl.getBoundingClientRect();
} else {
scrollerRect = {
top: 0,
bottom: this.scrollEl.innerHeight
};
}
const scrollerHeight = scrollerRect.bottom - scrollerRect.top;
if(!scrollerHeight) return;
const placeholderRect = this.placeholder.getBoundingClientRect();
if(placeholderRect.bottom - scrollerRect.bottom <= (+this.offset)){
this.$emit('loadMore');
}
}
最后
感谢阅读~