现象:在streaming初期,indicator会频繁出现再消失,2s后才稳定
原因分析:
- 每次
appendStreamChunk都会调用#smartScroll() - 内容高度变化导致滚动位置判断不稳定
- 没有防抖机制,导致频繁的状态切换
修复方案:
- 添加距离检查:如果距离底部小于20px,认为已经在底部,不需要滚动
- 状态变化检查:只有在状态真正发生变化时才更新
- 防抖机制:indicator显示/隐藏添加100ms防抖
现象:手动滚动后,内容更新完毕时仍会自动滚动到底部
原因分析:
finishStreamMessage()直接调用scrollToBottom(),没有考虑用户状态- 没有遵循智能滚动逻辑
修复方案:
- 将
finishStreamMessage()中的scrollToBottom()改为#smartScroll() - 确保遵循用户的滚动偏好
#smartScroll() {
// 如果用户正在滚动,不要自动滚动
if (this.#userScrolling) {
this.#showNewContentIndicator();
return;
}
// 检查是否真的需要滚动(避免频繁的滚动操作)
const { scrollTop, scrollHeight, clientHeight } = this.#container;
const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
// 如果距离底部小于20px,认为已经在底部,不需要滚动
if (distanceFromBottom < 20) {
this.#hideNewContentIndicator();
return;
}
// 如果启用了自动滚动,则滚动到底部
if (this.#autoScroll) {
this.scrollToBottom();
this.#hideNewContentIndicator();
} else {
// 否则显示新内容提示
this.#showNewContentIndicator();
}
}#checkScrollPosition() {
const { scrollTop, scrollHeight, clientHeight } = this.#container;
const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
// 如果距离底部小于50px,认为用户在关注底部内容
if (distanceFromBottom < 50) {
// 只有在状态发生变化时才更新
if (!this.#autoScroll) {
this.#enableAutoScroll();
this.#hideNewContentIndicator();
}
} else {
// 只有在状态发生变化时才更新
if (this.#autoScroll) {
this.#disableAutoScroll();
}
}
}#showNewContentIndicator() {
if (this.#newContentIndicator) {
// 防抖处理:避免频繁显示/隐藏
if (this.#indicatorTimeout) {
clearTimeout(this.#indicatorTimeout);
}
this.#indicatorTimeout = setTimeout(() => {
if (this.#newContentIndicator) {
this.#newContentIndicator.style.display = 'flex';
}
}, 100);
}
}finishStreamMessage() {
// ... 其他逻辑 ...
// 智能滚动:只在用户没有禁用自动滚动时才滚动
this.#smartScroll();
}- ❌ Indicator频繁闪烁,体验差
- ❌ 手动滚动后仍被强制滚动到底部
- ❌ 没有防抖机制,性能不佳
- ✅ Indicator稳定显示,不再闪烁
- ✅ 尊重用户的滚动偏好
- ✅ 添加防抖机制,性能优化
- ✅ 更精确的滚动位置判断
- 20px:用于判断是否已经在底部,避免不必要的滚动
- 50px:用于判断用户是否在关注底部内容
- 滚动检测:150ms防抖
- Indicator显示:100ms防抖
- 只有在状态真正发生变化时才更新
- 避免频繁的DOM操作和样式切换
这些修复大大改善了streaming聊天的滚动体验,让用户能够更舒适地控制阅读节奏。