diff --git a/apps/web/src/pages/ReaderPage.tsx b/apps/web/src/pages/ReaderPage.tsx index f4b46e3..ab4cb54 100644 --- a/apps/web/src/pages/ReaderPage.tsx +++ b/apps/web/src/pages/ReaderPage.tsx @@ -162,7 +162,7 @@ export function ReaderPage({ bookId }: { bookId: number }) { const changeZoom = useCallback((nextZoom: number | ((currentZoom: number) => number)) => { const stage = document.querySelector(".reader-stage") as HTMLElement | null; const scrollRatioX = stage && stage.scrollWidth > stage.clientWidth ? (stage.scrollLeft + stage.clientWidth / 2) / stage.scrollWidth : 0.5; - const scrollRatioY = stage && stage.scrollHeight > stage.clientHeight ? (stage.scrollTop + stage.clientHeight / 2) / stage.scrollHeight : 0.5; + const scrollRatioY = stage && stage.scrollHeight > stage.clientHeight ? stage.scrollTop / (stage.scrollHeight - stage.clientHeight) : 0; setZoom((currentZoom) => clampReaderZoom(typeof nextZoom === "function" ? nextZoom(currentZoom) : nextZoom)); @@ -170,7 +170,7 @@ export function ReaderPage({ bookId }: { bookId: number }) { requestAnimationFrame(() => { if (!stage) return; stage.scrollLeft = Math.max(0, stage.scrollWidth * scrollRatioX - stage.clientWidth / 2); - stage.scrollTop = Math.max(0, stage.scrollHeight * scrollRatioY - stage.clientHeight / 2); + stage.scrollTop = Math.max(0, (stage.scrollHeight - stage.clientHeight) * scrollRatioY); }); }); }, []); diff --git a/apps/web/src/pages/ReaderPageVerticalRestore.test.ts b/apps/web/src/pages/ReaderPageVerticalRestore.test.ts index 39bb1ea..d533831 100644 --- a/apps/web/src/pages/ReaderPageVerticalRestore.test.ts +++ b/apps/web/src/pages/ReaderPageVerticalRestore.test.ts @@ -8,4 +8,12 @@ describe("ReaderPage vertical restore", () => { expect(bookResetEffect).toContain("setPage(1)"); }); + + it("does not restore an unknown vertical scroll anchor to the middle of the book", () => { + const source = readFileSync(new URL("./ReaderPage.tsx", import.meta.url), "utf8"); + const changeZoomBlock = source.slice(source.indexOf("const changeZoom = useCallback"), source.indexOf(" const zoomControls", source.indexOf("const changeZoom = useCallback"))); + + expect(changeZoomBlock).toContain("scrollRatioY"); + expect(changeZoomBlock).not.toMatch(/scrollRatioY[\s\S]*:\s*0\.5/); + }); });