加载新消息,滚动条位置不变
在实现聊天记录的“加载更多”功能时,保持滚动条位置不变是一个常见的需求。这个功能的核心是在加载新消息后,确保用户的视角没有被突兀地改变。以下是实现这一功能的常用方法:
1. 计算并调整滚动条的位置
首先,在加载新消息前,计算当前滚动条的位置oldScrollTop
,然后在新消息加载完成后,计算滚动条的偏移量scrollOffset
,通过赋值调整滚动条位置来恢复用户的视角。
- 代码描述
const chatContainer = document.getElementById('chat-container');//window。聊天窗口
function loadMoreMessages() {
// 获取当前滚动条的偏移量
const oldScrollHeight = chatContainer.scrollHeight;
const oldScrollTop = chatContainer.scrollTop;
// 加载更多消息
// 模拟异步加载
setTimeout(() => {
// 这里插入新消息到DOM中
// chatContainer.innerHTML = newMessages + chatContainer.innerHTML;
// 计算新消息加载后增加的高度
const newScrollHeight = chatContainer.scrollHeight;
const scrollOffset = newScrollHeight - oldScrollHeight;
// 恢复滚动条到原来的位置
chatContainer.scrollTop = oldScrollTop + scrollOffset;
}, 500);
}
// 监听滚动事件,用户滑到顶端时触发加载更多消息
chatContainer.addEventListener('scroll', () => {
if (chatContainer.scrollTop === 0) {
loadMoreMessages();
}
});
需要注意的是异步渲染的问题。如果是在vue中就需要使用this.$nextTick()
,来计算渲染完成后的scrollHeight
例如:
setTimeout(()=>{
this.ChatList.unshift({
"id": 4,
"SessionDateTime": 1724822895499,
"SessionImage": "",
"SessionText": "你好",
"ResponseDateTime": 1724822895499,
"ResponseText": "你好!",
"State": 0
},{
"id": 5,
"SessionDateTime": 1724822895499,
"SessionImage": "",
"SessionText": "最近怎么样",
"ResponseDateTime": 1724822895499,
"ResponseText": "还好,你呢",
"State": 0
},{
"id": 6,
"SessionDateTime": 1724822895499,
"SessionImage": "",
"SessionText": "还行吧,一如既往",
"ResponseDateTime": 1724822895499,
"ResponseText": "下周我要去旅游,一起不",
"State": 0
})
this.$nextTick(() => {
const newScrollHeight = bodyDom.scrollHeight;
const scrollOffset = newScrollHeight - this.oldScrollHeight;
//计算赋值新的scrollTop;达到视口内容保持一致的效果。
bodyDom.scrollTop = this.oldScrollTop + scrollOffset;
})
},5000)
//滚动事件
handleScroll($event){
const { scrollHeight, scrollTop, clientHeight } = $event.target;
this.oldScrollHeight = scrollHeight;
this.oldScrollTop = scrollTop;
}
2. 使用虚拟滚动技术
对于大量聊天记录的加载,虚拟滚动是一个性能优化的选择。你可以使用像 react-window
或 react-virtualized
这样的库来只渲染可见部分的消息,并且在用户滚动时动态加载内容。
3. CSS 滚动行为优化
在某些情况下,可以使用 scroll-behavior
来确保滚动平滑:
#chat-container {
scroll-behavior: smooth;
}
不过,这个通常适用于让滚动变得更平滑,对于你提到的“加载更多”场景,还需要手动调整滚动条位置。
4. 最新消息渲染至视口
scrollIntoView
是一个非常实用的 DOM 方法,它的主要功能是让某个元素滚动到视图的可见区域。这个方法在各种需要确保某个元素可见的场景下都能派上用场,比如自动滚动到某个错误消息、定位到某个被点击的项目、或者在聊天窗口中滚动到最新的一条消息。
使用场景
- 自动滚动到错误提示:
- 表单提交后,如果有验证错误,可以自动滚动到第一个错误提示。
- 导航链接:
- 单页应用(SPA)中,点击导航链接后,可以平滑地滚动到相应的内容区域。
- 聊天应用:
- 加载新消息后,自动滚动到最新的消息位置。
- 固定头部或底部的元素:
- 确保在滚动操作中某个固定位置的元素保持可见。
scrollIntoView
的基本用法
语法:
element.scrollIntoView(alignToTop); // 简单用法
element.scrollIntoView(options); // 更详细的配置
alignToTop
(可选):
-
true
(默认):元素的顶部将与其滚动容器的可见区域顶部对齐。 -
false
:元素的底部将与其滚动容器的可见区域底部对齐。
options
(可选):这是一个包含更多选项的对象,用于控制滚动行为。
element.scrollIntoView({
behavior: 'smooth', // 'auto' or 'smooth'
block: 'start', // 'start', 'center', 'end', or 'nearest'
inline: 'nearest' // 'start', 'center', 'end', or 'nearest'
});
示例
- 滚动到元素顶部:
document.getElementById('targetElement').scrollIntoView(true);
- 滚动到元素底部:
document.getElementById('targetElement').scrollIntoView(false);
- 平滑滚动到元素:
document.getElementById('targetElement').scrollIntoView({
behavior: 'smooth',
block: 'start'
});
注意事项
- 浏览器支持:
scrollIntoView
是一个标准的浏览器 API,大多数现代浏览器都支持,但要确保在某些老旧浏览器中进行兼容性处理。 - 性能:当涉及大量内容或复杂的页面布局时,频繁调用
scrollIntoView
可能会影响性能,尤其是在低端设备上。对于这种情况,可以考虑优化调用频率或者使用虚拟滚动技术。
结论
scrollIntoView
非常适合那些需要在用户操作或页面事件后,将某个元素滚动到可视区域的场景。通过合理配置滚动行为和对齐方式,可以显著提升用户体验。但是我这里使用平滑过渡到滚动元素时,无法真正达到底部对齐,需要手动再滑动一下,因为业务效果不要求,就没具体研究,知道的好心人可以提点建议
总结
保持滚动条位置不变的关键是记录和恢复滚动条的高度差异。通过计算加载新消息前后的高度差,并调整滚动位置,可以有效地防止用户视角的跳动。