/**
|
* CInfiniteScroll
|
* @author Tevin
|
*/
|
|
<template>
|
<scroll-view
|
:id="elmId"
|
class="c-infinite-scroll"
|
:scrollY="true"
|
lowerThreshold="100"
|
@scroll="evt=>onScroll(evt)"
|
@touchstart="evt=>onTouch(evt)"
|
@touchmove="evt=>onTouch(evt)"
|
@touchend="evt=>onTouch(evt)"
|
@scrollToLower="evt=>onScrollToLower(evt)"
|
>
|
<view
|
class="c-infinite-top"
|
:class="touching?'touching':''"
|
:style="{height: touch.driftTop+'rem'}"
|
>
|
<view class="inner">
|
<view v-if="touch.dargState===2">
|
<AtActivityIndicator />
|
<text class="text">重新加载中...</text>
|
</view>
|
<view
|
class="tips"
|
v-else
|
:class="touch.dargState===0?'':'release'"
|
>
|
<view class="at-icon at-icon-arrow-down"></view>
|
<text class="text">下拉刷新</text>
|
<text class="text">释放刷新</text>
|
</view>
|
</view>
|
</view>
|
<view class="c-infinite-content">
|
<slot />
|
</view>
|
<view class="c-infinite-bottom">
|
<view
|
class="c-infinite-loading"
|
v-if="!ending"
|
>
|
<text v-show="loading">加载中,请稍后...</text>
|
</view>
|
<view v-if="ending">
|
<text
|
class="c-infinite-ended"
|
v-if="pageTotal > 0"
|
>-- 没有更多了 --</text>
|
<text
|
class="c-infinite-empty"
|
v-else
|
>暂无数据...</text>
|
</view>
|
</view>
|
</scroll-view>
|
</template>
|
|
<script>
|
import Taro from '@tarojs/taro';
|
import { AtActivityIndicator } from 'taro-ui-vue';
|
import { Tools } from '@components/common/Tools';
|
import './cInfiniteScroll.scss';
|
|
export default {
|
name: 'CInfiniteScroll',
|
components: {
|
AtActivityIndicator,
|
},
|
props: {
|
// 是否自动初始化(自动加载第一页)
|
autoInit: {
|
type: Boolean,
|
default: false,
|
},
|
// 发起加载页面数据回调
|
// 参数 {
|
// current: 当前页页码,
|
// next: 需要加载的页面页码,
|
// success: 当加载成功后页面调用, 回传参数 { pageTotal: 当前翻页总页数 }
|
// fail: 加载失败后页面调用
|
// }
|
onLoadMore: Function,
|
},
|
data() {
|
return {
|
elmId: 'cis-' + Tools.createGUID(),
|
// 初始化状态
|
inital: false,
|
// 当前页面
|
current: 0,
|
// 总共页面
|
pageTotal: 1,
|
// 加载中状态
|
loading: false,
|
// 是否结束状态
|
ending: false,
|
// 上次滚动位置
|
lastScrollTop: 0,
|
// 滚动节流器
|
scrollTimer: 0,
|
// 是否已滚动
|
scrolled: false,
|
// 滑屏数据
|
touch: {
|
startX: 0,
|
startY: 0,
|
lastY: 0,
|
dargState: 0,
|
driftTop: 0,
|
},
|
// 正在滑屏状态
|
touching: false,
|
};
|
},
|
methods: {
|
hanldeLoadMore() {
|
// 结束后跳过
|
if (this.ending) {
|
return;
|
}
|
// 加载中跳过
|
if (this.loading) {
|
return;
|
}
|
// 开始加载
|
this.inital = true;
|
this.loading = true;
|
setTimeout(() => {
|
this.onLoadMore({
|
current: this.current,
|
next: this.current + 1,
|
// 加载成功
|
success: options => {
|
this.$nextTick(() => {
|
this.loading = false;
|
// 没有数据
|
if (!options.pageTotal || options.pageTotal <= 0) {
|
this.ending = true;
|
this.pageTotal = 0;
|
}
|
// 有数据
|
else {
|
this.pageTotal = options.pageTotal;
|
this.current = this.current + 1;
|
// 已到最后一页
|
if (this.current >= this.pageTotal) {
|
this.ending = true;
|
}
|
}
|
// 如果下拉刷新,还原下拉
|
if (this.touch.dargState === 2) {
|
this.touch.driftTop = 0;
|
setTimeout(() => {
|
this.touch.dargState = 0;
|
}, 300);
|
}
|
});
|
},
|
// 加载失败
|
fail: () => {
|
this.loading = false;
|
this.ending = true;
|
this.pageTotal = 0;
|
// 如果下拉刷新,还原下拉
|
if (this.touch.dargState === 2) {
|
this.touch.driftTop = 0;
|
setTimeout(() => {
|
this.touch.dargState = 0;
|
}, 300);
|
}
|
},
|
});
|
}, 100);
|
},
|
onScroll(evt) {
|
// 已经滚动(滚动后禁止下拉刷新)
|
this.scrolled = true;
|
// 往上
|
if (this.lastScrollTop >= evt.detail.scrollTop) {
|
this.lastScrollTop = evt.detail.scrollTop;
|
return;
|
}
|
// 往下
|
else {
|
if (process.env.TARO_ENV !== 'h5') {
|
return;
|
}
|
const viewHeight = evt.target.offsetHeight;
|
const { scrollTop, scrollHeight } = evt.detail;
|
// 阀值 100 像素,向下滚动到最后 100 像素,识别为启动加载
|
if (viewHeight + scrollTop >= scrollHeight - 100) {
|
clearTimeout(this.scrollTimer);
|
this.scrollTimer = setTimeout(() => {
|
this.hanldeLoadMore();
|
}, 200);
|
}
|
}
|
},
|
// 小程序中滚动到底部
|
onScrollToLower() {
|
if (process.env.TARO_ENV !== 'weapp') {
|
return;
|
}
|
this.hanldeLoadMore();
|
},
|
// 手指拨动
|
onTouch(evt) {
|
// 拉动长度(少于这个值的时候下拉但是不刷新)
|
const deviationY = 50;
|
// 拉动的最大高度
|
const maxTransY = 100;
|
// 按下
|
if (evt.type === 'touchstart') {
|
this.touching = true;
|
// 手指按下,重置是否已滚动
|
this.scrolled = false;
|
if (evt.touches.length !== 1) {
|
return;
|
}
|
this.touch.startX = evt.touches[0].clientX;
|
this.touch.startY = evt.touches[0].clientY;
|
this.touch.lastY = evt.touches[0].clientY;
|
}
|
// 滑动
|
else if (evt.type === 'touchmove') {
|
// 已滚动跳过
|
if (this.scrolled) {
|
return;
|
}
|
// 加载中跳过
|
if (this.loading) {
|
return;
|
}
|
this.touching = true;
|
// 左右偏移判断(向下拉时,如果左右偏移超过阀值不执行下拉操作)
|
const ratio =
|
Math.abs(evt.touches[0].clientX - this.touch.startX) /
|
Math.abs(evt.touches[0].clientY - this.touch.startY);
|
const dragDown = this.touch.lastY - evt.touches[0].clientY < 0;
|
this.touch.lastY = evt.touches[0].clientY;
|
if (dragDown && ratio > 0.3) {
|
return;
|
}
|
// 拖动距离
|
const deltY = evt.touches[0].clientY - this.touch.startY;
|
// 拖动值=拖动距离/拖动倍率,使拖动的时候有粘滞的感觉
|
const transY = Math.min(Math.abs(deltY) / 4, maxTransY);
|
if (deltY > 0) {
|
this.touch.driftTop = (transY / maxTransY) * 5;
|
// 超过阀值,可以刷新
|
if (transY >= deviationY) {
|
this.touch.dargState = 1;
|
}
|
// 小于阀值,不能刷新
|
else {
|
this.touch.dargState = 0;
|
}
|
} else {
|
this.touch.driftTop = 0;
|
}
|
}
|
// 松开
|
else if (evt.type === 'touchend') {
|
this.touching = false;
|
// 已滚动跳过
|
if (this.scrolled) {
|
this.touch.driftTop = 0;
|
return;
|
}
|
// 加载中跳过
|
if (this.loading) {
|
this.touch.driftTop = 0;
|
return;
|
}
|
// 不刷新
|
if (this.touch.dargState === 0) {
|
this.touch.driftTop = 0;
|
return;
|
}
|
// 刷新数据
|
if (this.touch.dargState === 1) {
|
this.touch.driftTop = (deviationY / maxTransY) * 5;
|
// 重新加载
|
this.touch.dargState = 2;
|
this.ending = false;
|
this.current = 0;
|
this.hanldeLoadMore();
|
}
|
}
|
},
|
// 初始加载
|
$initScroll() {
|
// 已初始化,跳过
|
if (this.inital) {
|
return;
|
}
|
this.hanldeLoadMore();
|
},
|
// 刷新加载
|
$refresh(autoStart) {
|
Taro.pageScrollTo({
|
selector: '#' + this.elmId,
|
scrollTop: 0,
|
});
|
this.ending = false;
|
this.current = 0;
|
if (autoStart === 'off') {
|
this.inital = false;
|
} else {
|
this.hanldeLoadMore();
|
}
|
},
|
},
|
mounted() {
|
// 开启自动初始化
|
if (this.autoInit) {
|
this.hanldeLoadMore();
|
}
|
},
|
};
|
</script>
|