diff --git a/web/src/components/VideoGenerationPage.module.css b/web/src/components/VideoGenerationPage.module.css index 407a6cb..d72f20f 100644 --- a/web/src/components/VideoGenerationPage.module.css +++ b/web/src/components/VideoGenerationPage.module.css @@ -17,6 +17,10 @@ flex: 1; overflow-y: auto; overflow-x: hidden; + /* 关掉浏览器自动 scroll anchoring:往上加载历史时由 handleScroll 里的 + anchor 逻辑统一管,避免浏览器默认的 anchor 与我们手动 +diff 叠加, + 导致慢速滚动 / 慢拖滑动条时页面被推到最底部。 */ + overflow-anchor: none; } .emptyArea { diff --git a/web/src/components/VideoGenerationPage.tsx b/web/src/components/VideoGenerationPage.tsx index 26f6b24..1fd0cd3 100644 --- a/web/src/components/VideoGenerationPage.tsx +++ b/web/src/components/VideoGenerationPage.tsx @@ -22,6 +22,9 @@ export function VideoGenerationPage() { const scrollRef = useRef(null); const prevLastIdRef = useRef(null); const initialLoadRef = useRef(true); + // 防重入 flag:loadMore + anchor 期间,handleScroll 多次触发不再 schedule 多个 rAF, + // 避免 anchor 累加把页面推到底(慢速滚轮 / 慢拖滑动条场景)。 + const loadMoreInFlightRef = useRef(false); const savedScrollTop = useGenerationStore((s) => s.savedScrollTop); const saveScrollPosition = useGenerationStore((s) => s.saveScrollPosition); const [detailTaskId, setDetailTaskId] = useState(null); @@ -76,15 +79,20 @@ export function VideoGenerationPage() { const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight; setShowScrollBottom(distanceFromBottom > 300); - // Trigger loadMore when scrolled within 100px of the top - if (scrollRef.current.scrollTop < 100) { + // Trigger loadMore when scrolled within 100px of the top. + // ref flag 守卫:只有第一次进入分支时才 schedule loadMore + anchor; + // 后续 handleScroll(慢速操作下持续触发)直接跳过,避免多次 rAF 排队累加 +diff。 + if (scrollRef.current.scrollTop < 100 && !loadMoreInFlightRef.current) { + loadMoreInFlightRef.current = true; const el = scrollRef.current; const prevHeight = el.scrollHeight; loadMore().then(() => { - // After older tasks are prepended, restore visual position so user doesn't jump + // After older tasks are prepended, restore visual position so user doesn't jump. + // CSS overflow-anchor: none 已禁用浏览器自动 anchor,由这里独立完成。 requestAnimationFrame(() => { const diff = el.scrollHeight - prevHeight; if (diff > 0) el.scrollTop += diff; + loadMoreInFlightRef.current = false; }); }); }