Fix page jumping when scrolling continuous pagers

Both hardcoded thresholds (MIN_VISIBLE_PX 0 or 1) break the "first visible image" detection in different kind of ways.

With 1 it always incorrectly jumps to the next page when resuming a chapter, since the page to resume will bet at 0px.

With 0 it fails to detect the correct first visible image when scrolling (automatically not via wheel or touch) to another page.
This happens when there is no gap between the pages because in this case the start of the current image and the end of the previous image are at the same coordinate.
Thus, the previous image is still incorrectly detected as visible since the end still aligns with the viewports start/end
This commit is contained in:
schroda
2024-12-31 16:43:46 +01:00
parent c2c4115c9c
commit d6a3d02189
2 changed files with 43 additions and 10 deletions

View File

@@ -445,7 +445,23 @@ export class ReaderControls {
type: PageInViewportType,
readingDirection: ReadingDirection,
) {
const firstVisibleImageIndex = imageRefs.current.findIndex((image) => image && isPageInViewport(image, type));
const themeDirectionForReadingDirection = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection];
const firstVisibleImageIndex = imageRefs.current.findIndex(
(image) =>
image &&
isPageInViewport(
image,
type,
// the pages aren't always properly scrolled to the top which results in the leading page to still be slightly
// visible (e.g. 0.0123px) breaking the "first visible image" detection
{
bottom: 1,
left: getOptionForDirection(0, window.innerWidth + 1, themeDirectionForReadingDirection),
right: getOptionForDirection(1, 0, themeDirectionForReadingDirection),
},
),
);
const lastPage = imageRefs.current?.[imageRefs.current.length - 1];
const isEndReached = lastPage && isEndOfPageInViewport(lastPage, type, readingDirection);

View File

@@ -291,8 +291,15 @@ export const createReaderPage = (
/>
);
type InViewportThresholds = {
top?: number;
bottom?: number;
left?: number;
right?: number;
};
const getIsPageInViewportInfo = (
element: HTMLElement,
argThresholds?: InViewportThresholds,
): {
isLeftInViewport: boolean;
isRightInViewport: boolean;
@@ -303,15 +310,21 @@ const getIsPageInViewportInfo = (
} => {
const { top, bottom, left, right } = element.getBoundingClientRect();
const MIN_VISIBLE_PX = 0;
const thresholds = {
top: 0,
bottom: 0,
left: 0,
right: 0,
...argThresholds,
};
const isLeftInViewport = left >= MIN_VISIBLE_PX && left <= window.innerWidth;
const isRightInViewport = right >= MIN_VISIBLE_PX && right <= window.innerWidth;
const isFillingWidthViewportCompletely = left <= MIN_VISIBLE_PX && right >= window.innerWidth;
const isLeftInViewport = left >= thresholds.left && left <= window.innerWidth;
const isRightInViewport = right >= thresholds.right && right <= window.innerWidth;
const isFillingWidthViewportCompletely = left <= thresholds.left && right >= window.innerWidth;
const isTopInViewport = top >= MIN_VISIBLE_PX && top <= window.innerHeight;
const isBottomInViewport = bottom >= MIN_VISIBLE_PX && bottom <= window.innerHeight;
const isFillingHeightViewportCompletely = top <= MIN_VISIBLE_PX && bottom >= window.innerHeight;
const isTopInViewport = top >= thresholds.top && top <= window.innerHeight;
const isBottomInViewport = bottom >= thresholds.bottom && bottom <= window.innerHeight;
const isFillingHeightViewportCompletely = top <= thresholds.top && bottom >= window.innerHeight;
return {
isLeftInViewport,
@@ -323,7 +336,11 @@ const getIsPageInViewportInfo = (
};
};
export const isPageInViewport = (element: HTMLElement, type: PageInViewportType): boolean => {
export const isPageInViewport = (
element: HTMLElement,
type: PageInViewportType,
thresholds?: InViewportThresholds,
): boolean => {
const {
isLeftInViewport,
isRightInViewport,
@@ -331,7 +348,7 @@ export const isPageInViewport = (element: HTMLElement, type: PageInViewportType)
isTopInViewport,
isBottomInViewport,
isFillingHeightViewportCompletely,
} = getIsPageInViewportInfo(element);
} = getIsPageInViewportInfo(element, thresholds);
const isInViewportX = isLeftInViewport || isRightInViewport || isFillingWidthViewportCompletely;
const isInViewportY = isTopInViewport || isBottomInViewport || isFillingHeightViewportCompletely;