需求背景:给滚动到视野中的消息设置已读状态
实现方案:1.IntersectionObserver;2.监听scroll事件
1. 使用IntersectionObserver实现
- 实例化IntersectionObserver,添加观察者
const observer = new IntersectionObserver(entries => {
...
});
// 获取需要观察的每条消息的dom元素
const messageItemComps = this.$refs.messageItem;
messageItemComps.forEach(item => {
// 添加观察者
observer.observe(item.$el);
});
- 设置已读状态、移除观察者
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
// 当前消息的dom元素完全出现时设置消息为已读
if (entry.intersectionRatio === 1) {
const index = entry.target.dataset.index;
this.setMsgRead(this.messagesList[index]);
// 设置完成之后移除观察者
observer.unobserve(entry.target);
}
});
});
...
- 存在问题
- 多条消息未读的情况下,快速滚动会造成接口请求并发,等待时间长,性能不好
2. 消息批量设置已读(监听scroll事件)
**监听滚动容器的scroll事件,计算scrollTop + clientHeight区域可以容纳的消息数,批量设置已读**,参考如下:
// 获取滚动容易,监听滚动事件
const messageWrap = this.$refs.listWrap;
messageWrap.addEventListener('scroll', this.handleScroll);
滚动事件参考:
// 避免频繁触发可为scroll事件添加防抖
handleScroll: debounce(function() {
// 向上滚动高度
const scrollTop = this.$refs.listWrap.scrollTop;
// 屏幕高度
const clientHeight = document.body.clientHeight;
// 消息列表初始高度值
let height = 0;
// 滚动到的消息index
let index = 0;
const messageItemComps = this.$refs.messageItem;
for (let i = 0; i < messageItemComps.length; i++) {
const item = messageItemComps[i];
height = height + item.$el.offsetHeight;
if (height > (scrollTop + clientHeight)) {
index = i;
break;
}
}
// 消息列表高度在一屏内
if (height <= scrollTop + clientHeight) {
index = this.messagesList.length;
}
// 获取未读消息
const setReadMsgsList = this.messagesList.slice(0, index).filter(item => !item.isRead);
// 批量设置已读
this.setMsgReadBatch(setReadMsgsList);
}, 300),
注意:滚动事件滚动才会触发,首次打开视野内消息设置已读需要主动调用一次
最后要记得移除监听事件:
beforeDestroy() {
const messageWrap = this.$refs.listWrap;
messageWrap.removeEventListener('scroll', this.handleScroll);
}