diff --git a/frontend/src/readers/CbzReader.tsx b/frontend/src/readers/CbzReader.tsx index 15bc4d3..4f93455 100644 --- a/frontend/src/readers/CbzReader.tsx +++ b/frontend/src/readers/CbzReader.tsx @@ -21,7 +21,9 @@ function useStoredBool(key: string, def: boolean) { function useStoredPrefetch() { const [n, setN] = useState(() => { - const v = Number(localStorage.getItem("cbz-prefetch")); + const s = localStorage.getItem("cbz-prefetch"); + if (s === null) return 2; // 键缺失 ≠ 0:新用户默认 2 + const v = Number(s); return Number.isInteger(v) && v >= 0 && v <= 3 ? v : 2; }); useEffect(() => localStorage.setItem("cbz-prefetch", String(n)), [n]); @@ -105,6 +107,8 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome [chapters, count, ci, continuous, prefetch], ); const locked = !!chapters && chapters.length >= 2 && !continuous; + // 锁章下窗外前缀页不渲染 → scrollTop/页 top 一律窗口相对;读 ph(全局前缀和)前先加回 anchor + const anchor = locked ? ph.offset(w.start) : 0; // 容器尺寸自适应:回调 ref 在滚动盒真正挂载时才观测(等页数请求期间的早退渲染里没有这个节点) const roRef = useRef(null); @@ -136,7 +140,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome if (!el) return; const t = pending.current; pending.current = null; - el.scrollTop = ph.offset(t); + el.scrollTop = ph.offset(t) - anchor; }, [ci, ph]); // 滚动事件按帧合并;预取等滚动停稳再做——拖拽途中逐事件拉图=带宽/解码风暴 @@ -149,7 +153,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome const t = el.scrollTop; setTop(t); if (count) { - curRef.current = ph.pageAt(t + vh / 2); + curRef.current = ph.pageAt(t + anchor + vh / 2); if (!locked) setCi(chapterIndexAt(chapters, curRef.current)); // 连读/扁平:当前章随滚动走(目录计数高亮) saver.report({ page: curRef.current }, (curRef.current + 1) / count); } @@ -181,7 +185,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome }); } - const cur = ph.pageAt(top + vh / 2); + const cur = ph.pageAt(top + anchor + vh / 2); useEffect(() => { if (toc) activeRow.current?.scrollIntoView({ block: "center" }); @@ -190,7 +194,10 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome function scrollToPage(i: number, smooth: boolean) { const el = boxRef.current; if (!el) return; - el.scrollTo({ top: ph.offset(Math.max(w.start, Math.min(w.end - 1, i))), behavior: smooth ? "smooth" : "auto" }); + el.scrollTo({ + top: ph.offset(Math.max(w.start, Math.min(w.end - 1, i))) - anchor, + behavior: smooth ? "smooth" : "auto", + }); } // 统一定位入口:锁章下目标页在窗外 = 显式切章(滑条/书签/目录/章末卡片/恢复进度皆此一路径) @@ -232,14 +239,14 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome ); const fmt: string = book.page_url_fmt; - const [a0, b0] = ph.range(top, vh, 2); + const [a0, b0] = ph.range(top + anchor, vh, 2); // 视口位置换回全局空间再查页(锁章下 top 是窗口相对的) const a = Math.max(a0, w.start); const b = Math.min(b0, w.end); // 可见页永不超过章末;窗外页在隐藏预读层 - const contentH = locked ? ph.offset(w.end) + vh : ph.total(); + const contentH = locked ? ph.offset(w.end) - anchor + vh : ph.total(); function measure(i: number, h: number) { const el = boxRef.current; - const topY = el ? el.scrollTop : 0; + const topY = el ? el.scrollTop + anchor : 0; // 与 ph.offset 同处全局空间比较 const oldEst = ph.estHeight; const topOfI = ph.offset(i); const d = ph.set(i, h); @@ -247,7 +254,9 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome // 让未量高页的估高收敛到实测页高:同开本漫画量一页后 d≈0,拖拽滚动条不再逐帧打架 if (width > 0 && Math.abs(h - oldEst) / oldEst > 0.03) { const n = ph.setEst(h, topY); - if (el && n) applyShift(n * (h - oldEst)); + // n×Δest 是全局位移;锁章下 anchor 同步变了(前缀窗外页),那部分不影响窗口相对几何,须扣减 + const corr = n * (h - oldEst) - (ph.offset(w.start) - anchor); + if (el && corr) applyShift(corr); ratio.current = h / width; } bump((x) => x + 1); @@ -270,7 +279,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome {Array.from({ length: Math.max(0, b - a) }, (_, k) => { const i = a + k; return ( -
+
measure(i, h)} />
); @@ -278,7 +287,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome {locked && (
{w.end >= count ? ( — 全书完 — @@ -295,7 +304,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome {Array.from({ length: w.mountEnd - w.end }, (_, k) => { const i = w.end + k; return ( -
+
measure(i, h)} />
);