merge: fix/39-reader-vertical-scroll-tracking dans develop (suivi de page vertical assoupli — 4e correctif #39)

This commit is contained in:
Git Agent
2026-08-26 11:35:30 +02:00
2 changed files with 50 additions and 28 deletions

View File

@ -31,6 +31,7 @@ export function CbzReader({
const pendingVerticalAnchorRef = useRef(false);
const verticalTrackingReadyRef = useRef(false);
const verticalUserScrollRef = useRef(false);
const verticalAnchorTargetPageRef = useRef<number | null>(null);
const verticalAnchorFrameRef = useRef<number | null>(null);
const verticalAnchorAttemptRef = useRef(0);
const verticalAnchorStableFramesRef = useRef(0);
@ -82,6 +83,23 @@ export function CbzReader({
if (verticalAnchorFrameRef.current !== null) cancelAnimationFrame(verticalAnchorFrameRef.current);
verticalAnchorFrameRef.current = null;
}, []);
const commitVisiblePage = useCallback(
(stage: HTMLElement, allowInitialCommit = false) => {
if (!pages || !verticalTrackingReadyRef.current) return;
if (!allowInitialCommit && !verticalUserScrollRef.current) return;
const stageRect = stage.getBoundingClientRect();
const visiblePage = majorityVisiblePage(
Array.from(stage.querySelectorAll<HTMLElement>("[data-reader-page]")).map((element) => {
const rect = element.getBoundingClientRect();
return { page: Number(element.dataset.readerPage), top: rect.top, bottom: rect.bottom };
}),
stageRect.top,
stageRect.bottom
);
if (visiblePage && visiblePage !== currentPage) onPageCommit(clampReaderPage(visiblePage, pages.pageCount), pages.pageCount, 1, "queued");
},
[currentPage, onPageCommit, pages]
);
const stabilizeVerticalAnchor = useCallback(
(targetPage: number) => {
clearVerticalAnchorFrame();
@ -106,6 +124,7 @@ export function CbzReader({
if (verticalAnchorStableFramesRef.current >= VERTICAL_ANCHOR_STABLE_FRAMES) {
verticalTrackingReadyRef.current = true;
commitVisiblePage(stage, true);
return;
}
verticalAnchorAttemptRef.current += 1;
@ -116,7 +135,7 @@ export function CbzReader({
verticalAnchorFrameRef.current = requestAnimationFrame(measure);
},
[clearVerticalAnchorFrame]
[clearVerticalAnchorFrame, commitVisiblePage]
);
const go = useCallback(
@ -178,10 +197,11 @@ export function CbzReader({
pendingVerticalAnchorRef.current = false;
verticalTrackingReadyRef.current = false;
verticalUserScrollRef.current = false;
verticalAnchorTargetPageRef.current = null;
clearVerticalAnchorFrame();
return;
}
if (previousModeRef.current !== "vertical") {
if (previousModeRef.current !== "vertical" || (!verticalUserScrollRef.current && verticalAnchorTargetPageRef.current !== currentPage)) {
pendingVerticalAnchorRef.current = true;
verticalTrackingReadyRef.current = false;
verticalUserScrollRef.current = false;
@ -194,6 +214,7 @@ export function CbzReader({
return;
}
pendingVerticalAnchorRef.current = false;
verticalAnchorTargetPageRef.current = currentPage;
stabilizeVerticalAnchor(currentPage);
}
previousModeRef.current = mode;
@ -213,17 +234,7 @@ export function CbzReader({
const keyTarget = typeof window === "undefined" ? null : window;
const updateVisiblePage = () => {
frameId = 0;
if (!verticalTrackingReadyRef.current || !verticalUserScrollRef.current) return;
const stageRect = stage.getBoundingClientRect();
const visiblePage = majorityVisiblePage(
Array.from(stage.querySelectorAll<HTMLElement>("[data-reader-page]")).map((element) => {
const rect = element.getBoundingClientRect();
return { page: Number(element.dataset.readerPage), top: rect.top, bottom: rect.bottom };
}),
stageRect.top,
stageRect.bottom
);
if (visiblePage && visiblePage !== currentPage) onPageCommit(clampReaderPage(visiblePage, pages.pageCount), pages.pageCount, 1, "queued");
commitVisiblePage(stage);
};
const onScroll = () => {
if (frameId) return;
@ -243,7 +254,7 @@ export function CbzReader({
keyTarget?.removeEventListener("keydown", markUserScrollKey);
stage.removeEventListener("scroll", onScroll);
};
}, [currentPage, mode, onPageCommit, pages]);
}, [commitVisiblePage, mode, pages]);
useEffect(() => () => clearVerticalAnchorFrame(), [clearVerticalAnchorFrame]);

View File

@ -157,6 +157,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
const pendingVerticalAnchorRef = useRef(false);
const verticalTrackingReadyRef = useRef(false);
const verticalUserScrollRef = useRef(false);
const verticalAnchorTargetPageRef = useRef<number | null>(null);
const verticalAnchorFrameRef = useRef<number | null>(null);
const verticalAnchorAttemptRef = useRef(0);
const verticalAnchorStableFramesRef = useRef(0);
@ -178,6 +179,23 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
if (verticalAnchorFrameRef.current !== null) cancelAnimationFrame(verticalAnchorFrameRef.current);
verticalAnchorFrameRef.current = null;
}, []);
const commitVisiblePage = useCallback(
(stage: HTMLElement, allowInitialCommit = false) => {
if (!verticalTrackingReadyRef.current) return;
if (!allowInitialCommit && !verticalUserScrollRef.current) return;
const stageRect = stage.getBoundingClientRect();
const visiblePage = majorityVisiblePage(
Array.from(stage.querySelectorAll<HTMLElement>("[data-reader-page]")).map((element) => {
const rect = element.getBoundingClientRect();
return { page: Number(element.dataset.readerPage), top: rect.top, bottom: rect.bottom };
}),
stageRect.top,
stageRect.bottom
);
if (visiblePage && visiblePage !== currentPage) onPageCommit(clampReaderPage(visiblePage, pages), pages, 1, "queued");
},
[currentPage, onPageCommit, pages]
);
const stabilizeVerticalAnchor = useCallback(
(targetPage: number) => {
clearVerticalAnchorFrame();
@ -202,6 +220,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
if (verticalAnchorStableFramesRef.current >= VERTICAL_ANCHOR_STABLE_FRAMES) {
verticalTrackingReadyRef.current = true;
commitVisiblePage(stage, true);
return;
}
verticalAnchorAttemptRef.current += 1;
@ -212,7 +231,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
verticalAnchorFrameRef.current = requestAnimationFrame(measure);
},
[clearVerticalAnchorFrame]
[clearVerticalAnchorFrame, commitVisiblePage]
);
const go = useCallback(
@ -244,10 +263,11 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
pendingVerticalAnchorRef.current = false;
verticalTrackingReadyRef.current = false;
verticalUserScrollRef.current = false;
verticalAnchorTargetPageRef.current = null;
clearVerticalAnchorFrame();
return;
}
if (previousModeRef.current !== "vertical") {
if (previousModeRef.current !== "vertical" || (!verticalUserScrollRef.current && verticalAnchorTargetPageRef.current !== currentPage)) {
pendingVerticalAnchorRef.current = true;
verticalTrackingReadyRef.current = false;
verticalUserScrollRef.current = false;
@ -260,6 +280,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
return;
}
pendingVerticalAnchorRef.current = false;
verticalAnchorTargetPageRef.current = currentPage;
stabilizeVerticalAnchor(currentPage);
}
previousModeRef.current = mode;
@ -279,17 +300,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
const keyTarget = typeof window === "undefined" ? null : window;
const updateVisiblePage = () => {
frameId = 0;
if (!verticalTrackingReadyRef.current || !verticalUserScrollRef.current) return;
const stageRect = stage.getBoundingClientRect();
const visiblePage = majorityVisiblePage(
Array.from(stage.querySelectorAll<HTMLElement>("[data-reader-page]")).map((element) => {
const rect = element.getBoundingClientRect();
return { page: Number(element.dataset.readerPage), top: rect.top, bottom: rect.bottom };
}),
stageRect.top,
stageRect.bottom
);
if (visiblePage && visiblePage !== currentPage) onPageCommit(clampReaderPage(visiblePage, pages), pages, 1, "queued");
commitVisiblePage(stage);
};
const onScroll = () => {
if (frameId) return;
@ -309,7 +320,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
keyTarget?.removeEventListener("keydown", markUserScrollKey);
stage.removeEventListener("scroll", onScroll);
};
}, [currentPage, mode, onPageCommit, pages]);
}, [commitVisiblePage, mode, pages]);
useEffect(() => () => clearVerticalAnchorFrame(), [clearVerticalAnchorFrame]);