diff --git a/src/modules/reader/services/ReaderControls.ts b/src/modules/reader/services/ReaderControls.ts index fc6d30b6..ef3b4b4a 100644 --- a/src/modules/reader/services/ReaderControls.ts +++ b/src/modules/reader/services/ReaderControls.ts @@ -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); diff --git a/src/modules/reader/utils/ReaderPager.utils.tsx b/src/modules/reader/utils/ReaderPager.utils.tsx index f098fab7..e615a2ae 100644 --- a/src/modules/reader/utils/ReaderPager.utils.tsx +++ b/src/modules/reader/utils/ReaderPager.utils.tsx @@ -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;