需求
- 列表需要自动滚动
- 在手动滚动的时候,需要暂停自动滚动
- 暂停自动滚动后,3秒内继续自动滚动
- 滚动到第50条数据时,回到列表顶部
组件
在项目中采用z-paging
组件来处理列表,因此是基于z-paging
来实现以上需求
设计思路
难点
自动滚动这个需求本身不复杂,不过有以下几个问题
z-paging
组件的scrollY
方法不能指定滚动的过渡时间- ··z-paging··组件可以设置
usePageScroll
为true
,然后当手动执行scrollY
的时候,内部会调用uni.pageScrollTo
,此方法默认过度时间是300ms
,但是不能手动停止
解决思路
- 如果要做到平滑过度,那必然需要使用uni的页面滚动,同时为了能及时停止自动滚动,则必须修改z-paging源码,让
scroll
的时间可以动态传递(这个时间需要不断测试校准,以期达到最好效果) - 手动滚动时,需要停止自动滚动。则需要监听
onPageScroll
(使用了usePageScroll之后,需要在页面的onPageScroll(e)事件中调用this.$refs.paging.updatePageScrollTop(e.scrollTop);),并且判断 此次偏移量是否是用户触发的(经过校准,可以通过这个条件来判定e.scrollTop - this.scrollTop > 40 || e.scrollToTop - this.scrollTop < -4
) - 在滚动到50条的时候,回到顶部。则可以通过
z-paging
组件的listChange
获取到第50
条数据,结合uni-app
的节点布局交互方法createIntersectionObserver
监听渲染这条数据的组件是否出现在屏幕上
部分代码实现
核心Template
<!-- z-paging -->
<z-paging
ref="paging"
class="offer-warp"
v-model="dataList"
:default-page-size="pageSize"
:usePageScroll="true"
@listChange="listChange"
>
<!-- 其他业务代码,其中item-view是列表的cell项 -->
<item-view
:id="`list-${item.id}`"
v-for="item in tradingList"
:data="item"
:key="item.id"
/>
</z-paging>
核心JavaScript
data() {
return {
dataList: [],
pageSize: 20, //分页条数
interval: undefined,
scrollY: 0,
enableAutoScroll: true,
itemScrollObserver: undefined,
};
},
onShow() {
this.enableAutoScroll = true;
this.$nextTick(() => {
this.startScroll();
});
},
onHide() {
clearInterval(this.interval);
},
onUnload() {
clearInterval(this.interval);
},
methods:{
listChange(e) {
this.enableAutoScroll = true;
// 滑动到50条的时候,滑动到顶部,重新滚动
if (e.length >= 50) {
let item = e[50];
this.createNodeObsever(item.id);
}
this.reloadTimeForScroll(300);
},
async getListViewHeight() {
const query = uni.createSelectorQuery().in(this);
return new Promise((resolve) => {
query
.select('#idRefList')
.boundingClientRect((data) => {
console.log('得到布局位置信息' + JSON.stringify(data));
let height = data.height;
console.log('节点离页面顶部的距离为' + data.top, this.interval);
resolve(height);
})
.exec();
});
},
async getNodeInfo(section) {
const query = uni.createSelectorQuery().in(this);
return new Promise((resolve) => {
query
.select(section)
.boundingClientRect((data) => {
console.log('getNodeInfo section' + JSON.stringify(data));
resolve(data);
})
.exec();
});
},
reloadTimeForScroll(holdTime = 5000) {
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.enableAutoScroll = true;
this.$nextTick(() => {
this.startScroll();
});
}, holdTime);
},
/**
* 1.获取当前组件真实高度height
* 2.一定时间内往下滑动大概2个cell的高度 step(500毫秒)
* 3.滑动完成后,判断scrollTop再加上一定高度是否比height更大
* 3.1 scrollTop + step >=height 再次获取 组件高度,并判断是否滑动
* 3.2 scrollTop + step < height 继续滑动
*
*/
async startScroll() {
const height = await this.getListViewHeight();
const step = 6;
clearInterval(this.interval);
if (!this.enableAutoScroll) {
return;
}
const time = 1000 / 10;
this.interval = setInterval(() => {
let scrollTop = this.scrollTop + step;
if (!this.enableAutoScroll || height < 800) {
clearInterval(this.interval);
return;
}
if (scrollTop < height) {
this.scrollTop = scrollTop;
}
uni.pageScrollTo({
scrollTop: scrollTop,
duration: time,
});
}, time);
},
onPageScroll(e) {
val = { detail: e };
if (e.scrollTop - this.scrollTop > 40 || e.scrollToTop - this.scrollTop < -4) {
this.enableAutoScroll = false;
this.reloadTimeForScroll();
}
},
}