fix(web): lecteurs CBZ/CBR et PDF — assouplir le suivi de page en vertical: commit initial après stabilisation de l'ancrage et ré-ancrage si la page change sans scroll utilisateur (4e correctif bug #39)
This commit is contained in:
@ -31,6 +31,7 @@ export function CbzReader({
|
|||||||
const pendingVerticalAnchorRef = useRef(false);
|
const pendingVerticalAnchorRef = useRef(false);
|
||||||
const verticalTrackingReadyRef = useRef(false);
|
const verticalTrackingReadyRef = useRef(false);
|
||||||
const verticalUserScrollRef = useRef(false);
|
const verticalUserScrollRef = useRef(false);
|
||||||
|
const verticalAnchorTargetPageRef = useRef<number | null>(null);
|
||||||
const verticalAnchorFrameRef = useRef<number | null>(null);
|
const verticalAnchorFrameRef = useRef<number | null>(null);
|
||||||
const verticalAnchorAttemptRef = useRef(0);
|
const verticalAnchorAttemptRef = useRef(0);
|
||||||
const verticalAnchorStableFramesRef = useRef(0);
|
const verticalAnchorStableFramesRef = useRef(0);
|
||||||
@ -82,6 +83,23 @@ export function CbzReader({
|
|||||||
if (verticalAnchorFrameRef.current !== null) cancelAnimationFrame(verticalAnchorFrameRef.current);
|
if (verticalAnchorFrameRef.current !== null) cancelAnimationFrame(verticalAnchorFrameRef.current);
|
||||||
verticalAnchorFrameRef.current = null;
|
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(
|
const stabilizeVerticalAnchor = useCallback(
|
||||||
(targetPage: number) => {
|
(targetPage: number) => {
|
||||||
clearVerticalAnchorFrame();
|
clearVerticalAnchorFrame();
|
||||||
@ -106,6 +124,7 @@ export function CbzReader({
|
|||||||
|
|
||||||
if (verticalAnchorStableFramesRef.current >= VERTICAL_ANCHOR_STABLE_FRAMES) {
|
if (verticalAnchorStableFramesRef.current >= VERTICAL_ANCHOR_STABLE_FRAMES) {
|
||||||
verticalTrackingReadyRef.current = true;
|
verticalTrackingReadyRef.current = true;
|
||||||
|
commitVisiblePage(stage, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verticalAnchorAttemptRef.current += 1;
|
verticalAnchorAttemptRef.current += 1;
|
||||||
@ -116,7 +135,7 @@ export function CbzReader({
|
|||||||
|
|
||||||
verticalAnchorFrameRef.current = requestAnimationFrame(measure);
|
verticalAnchorFrameRef.current = requestAnimationFrame(measure);
|
||||||
},
|
},
|
||||||
[clearVerticalAnchorFrame]
|
[clearVerticalAnchorFrame, commitVisiblePage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const go = useCallback(
|
const go = useCallback(
|
||||||
@ -178,10 +197,11 @@ export function CbzReader({
|
|||||||
pendingVerticalAnchorRef.current = false;
|
pendingVerticalAnchorRef.current = false;
|
||||||
verticalTrackingReadyRef.current = false;
|
verticalTrackingReadyRef.current = false;
|
||||||
verticalUserScrollRef.current = false;
|
verticalUserScrollRef.current = false;
|
||||||
|
verticalAnchorTargetPageRef.current = null;
|
||||||
clearVerticalAnchorFrame();
|
clearVerticalAnchorFrame();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (previousModeRef.current !== "vertical") {
|
if (previousModeRef.current !== "vertical" || (!verticalUserScrollRef.current && verticalAnchorTargetPageRef.current !== currentPage)) {
|
||||||
pendingVerticalAnchorRef.current = true;
|
pendingVerticalAnchorRef.current = true;
|
||||||
verticalTrackingReadyRef.current = false;
|
verticalTrackingReadyRef.current = false;
|
||||||
verticalUserScrollRef.current = false;
|
verticalUserScrollRef.current = false;
|
||||||
@ -194,6 +214,7 @@ export function CbzReader({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pendingVerticalAnchorRef.current = false;
|
pendingVerticalAnchorRef.current = false;
|
||||||
|
verticalAnchorTargetPageRef.current = currentPage;
|
||||||
stabilizeVerticalAnchor(currentPage);
|
stabilizeVerticalAnchor(currentPage);
|
||||||
}
|
}
|
||||||
previousModeRef.current = mode;
|
previousModeRef.current = mode;
|
||||||
@ -213,17 +234,7 @@ export function CbzReader({
|
|||||||
const keyTarget = typeof window === "undefined" ? null : window;
|
const keyTarget = typeof window === "undefined" ? null : window;
|
||||||
const updateVisiblePage = () => {
|
const updateVisiblePage = () => {
|
||||||
frameId = 0;
|
frameId = 0;
|
||||||
if (!verticalTrackingReadyRef.current || !verticalUserScrollRef.current) return;
|
commitVisiblePage(stage);
|
||||||
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");
|
|
||||||
};
|
};
|
||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
if (frameId) return;
|
if (frameId) return;
|
||||||
@ -243,7 +254,7 @@ export function CbzReader({
|
|||||||
keyTarget?.removeEventListener("keydown", markUserScrollKey);
|
keyTarget?.removeEventListener("keydown", markUserScrollKey);
|
||||||
stage.removeEventListener("scroll", onScroll);
|
stage.removeEventListener("scroll", onScroll);
|
||||||
};
|
};
|
||||||
}, [currentPage, mode, onPageCommit, pages]);
|
}, [commitVisiblePage, mode, pages]);
|
||||||
|
|
||||||
useEffect(() => () => clearVerticalAnchorFrame(), [clearVerticalAnchorFrame]);
|
useEffect(() => () => clearVerticalAnchorFrame(), [clearVerticalAnchorFrame]);
|
||||||
|
|
||||||
|
|||||||
@ -157,6 +157,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
|
|||||||
const pendingVerticalAnchorRef = useRef(false);
|
const pendingVerticalAnchorRef = useRef(false);
|
||||||
const verticalTrackingReadyRef = useRef(false);
|
const verticalTrackingReadyRef = useRef(false);
|
||||||
const verticalUserScrollRef = useRef(false);
|
const verticalUserScrollRef = useRef(false);
|
||||||
|
const verticalAnchorTargetPageRef = useRef<number | null>(null);
|
||||||
const verticalAnchorFrameRef = useRef<number | null>(null);
|
const verticalAnchorFrameRef = useRef<number | null>(null);
|
||||||
const verticalAnchorAttemptRef = useRef(0);
|
const verticalAnchorAttemptRef = useRef(0);
|
||||||
const verticalAnchorStableFramesRef = 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);
|
if (verticalAnchorFrameRef.current !== null) cancelAnimationFrame(verticalAnchorFrameRef.current);
|
||||||
verticalAnchorFrameRef.current = null;
|
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(
|
const stabilizeVerticalAnchor = useCallback(
|
||||||
(targetPage: number) => {
|
(targetPage: number) => {
|
||||||
clearVerticalAnchorFrame();
|
clearVerticalAnchorFrame();
|
||||||
@ -202,6 +220,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
|
|||||||
|
|
||||||
if (verticalAnchorStableFramesRef.current >= VERTICAL_ANCHOR_STABLE_FRAMES) {
|
if (verticalAnchorStableFramesRef.current >= VERTICAL_ANCHOR_STABLE_FRAMES) {
|
||||||
verticalTrackingReadyRef.current = true;
|
verticalTrackingReadyRef.current = true;
|
||||||
|
commitVisiblePage(stage, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verticalAnchorAttemptRef.current += 1;
|
verticalAnchorAttemptRef.current += 1;
|
||||||
@ -212,7 +231,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
|
|||||||
|
|
||||||
verticalAnchorFrameRef.current = requestAnimationFrame(measure);
|
verticalAnchorFrameRef.current = requestAnimationFrame(measure);
|
||||||
},
|
},
|
||||||
[clearVerticalAnchorFrame]
|
[clearVerticalAnchorFrame, commitVisiblePage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const go = useCallback(
|
const go = useCallback(
|
||||||
@ -244,10 +263,11 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
|
|||||||
pendingVerticalAnchorRef.current = false;
|
pendingVerticalAnchorRef.current = false;
|
||||||
verticalTrackingReadyRef.current = false;
|
verticalTrackingReadyRef.current = false;
|
||||||
verticalUserScrollRef.current = false;
|
verticalUserScrollRef.current = false;
|
||||||
|
verticalAnchorTargetPageRef.current = null;
|
||||||
clearVerticalAnchorFrame();
|
clearVerticalAnchorFrame();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (previousModeRef.current !== "vertical") {
|
if (previousModeRef.current !== "vertical" || (!verticalUserScrollRef.current && verticalAnchorTargetPageRef.current !== currentPage)) {
|
||||||
pendingVerticalAnchorRef.current = true;
|
pendingVerticalAnchorRef.current = true;
|
||||||
verticalTrackingReadyRef.current = false;
|
verticalTrackingReadyRef.current = false;
|
||||||
verticalUserScrollRef.current = false;
|
verticalUserScrollRef.current = false;
|
||||||
@ -260,6 +280,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pendingVerticalAnchorRef.current = false;
|
pendingVerticalAnchorRef.current = false;
|
||||||
|
verticalAnchorTargetPageRef.current = currentPage;
|
||||||
stabilizeVerticalAnchor(currentPage);
|
stabilizeVerticalAnchor(currentPage);
|
||||||
}
|
}
|
||||||
previousModeRef.current = mode;
|
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 keyTarget = typeof window === "undefined" ? null : window;
|
||||||
const updateVisiblePage = () => {
|
const updateVisiblePage = () => {
|
||||||
frameId = 0;
|
frameId = 0;
|
||||||
if (!verticalTrackingReadyRef.current || !verticalUserScrollRef.current) return;
|
commitVisiblePage(stage);
|
||||||
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");
|
|
||||||
};
|
};
|
||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
if (frameId) return;
|
if (frameId) return;
|
||||||
@ -309,7 +320,7 @@ export function PdfReader({ url, page, backHref, zoom, mode, onPageCommit, onCon
|
|||||||
keyTarget?.removeEventListener("keydown", markUserScrollKey);
|
keyTarget?.removeEventListener("keydown", markUserScrollKey);
|
||||||
stage.removeEventListener("scroll", onScroll);
|
stage.removeEventListener("scroll", onScroll);
|
||||||
};
|
};
|
||||||
}, [currentPage, mode, onPageCommit, pages]);
|
}, [commitVisiblePage, mode, pages]);
|
||||||
|
|
||||||
useEffect(() => () => clearVerticalAnchorFrame(), [clearVerticalAnchorFrame]);
|
useEffect(() => () => clearVerticalAnchorFrame(), [clearVerticalAnchorFrame]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user