Ignore trailing image resize for scroll preservation
Resizes of elements below the current scroll position do not cause a layout shift; thus, they can be ignored. This was a problem when using tap zone click scrolling, because the scrolling was aborted by the scroll position preservation
This commit is contained in:
@@ -287,7 +287,6 @@ const BaseReaderViewer = forwardRef(
|
|||||||
currentPageIndex,
|
currentPageIndex,
|
||||||
readingMode,
|
readingMode,
|
||||||
readingDirection,
|
readingDirection,
|
||||||
isContinuousReadingModeActive,
|
|
||||||
setPageToScrollToIndex,
|
setPageToScrollToIndex,
|
||||||
pageScaleMode,
|
pageScaleMode,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { RefObject, useEffect, useLayoutEffect, useRef } from 'react';
|
|||||||
import { ReaderPageScaleMode, ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
import { ReaderPageScaleMode, ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||||
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types';
|
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types';
|
||||||
import {
|
import {
|
||||||
|
isContinuousReadingMode,
|
||||||
isContinuousVerticalReadingMode,
|
isContinuousVerticalReadingMode,
|
||||||
isHeightPageScaleMode,
|
isHeightPageScaleMode,
|
||||||
isReaderWidthEditable,
|
isReaderWidthEditable,
|
||||||
@@ -72,10 +73,7 @@ const usePreserveOnWindowResize = (
|
|||||||
}, [readingMode, pageScaleMode, pageIndex]);
|
}, [readingMode, pageScaleMode, pageIndex]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const usePreserveOnReaderViewerElementMutation = (
|
const usePreserveOnLeadingPageRender = (scrollElementRef: RefObject<HTMLElement | null>, readingMode: ReadingMode) => {
|
||||||
scrollElementRef: RefObject<HTMLElement | null>,
|
|
||||||
isContinuousReadingModeActive: boolean,
|
|
||||||
) => {
|
|
||||||
const scrollPosition = useRef<{
|
const scrollPosition = useRef<{
|
||||||
left: number;
|
left: number;
|
||||||
top: number;
|
top: number;
|
||||||
@@ -90,6 +88,9 @@ const usePreserveOnReaderViewerElementMutation = (
|
|||||||
visibleElementTop: 0,
|
visibleElementTop: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode);
|
||||||
|
const isContinuousVerticalReadingModeActive = isContinuousVerticalReadingMode(readingMode);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const scrollElement = scrollElementRef.current;
|
const scrollElement = scrollElementRef.current;
|
||||||
|
|
||||||
@@ -113,20 +114,42 @@ const usePreserveOnReaderViewerElementMutation = (
|
|||||||
return () => scrollElement.removeEventListener('scroll', onScroll);
|
return () => scrollElement.removeEventListener('scroll', onScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useEffect(() => {
|
||||||
const scrollElement = scrollElementRef.current;
|
const scrollElement = scrollElementRef.current;
|
||||||
|
|
||||||
if (!scrollElement || !isContinuousReadingModeActive) {
|
if (!scrollElement || !isContinuousReadingModeActive) {
|
||||||
return () => {};
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const preserveScrollPosition = () => {
|
const preserveScrollPosition: ResizeObserverCallback = (entries) => {
|
||||||
const { left, top, visibleElement, visibleElementLeft, visibleElementTop } = scrollPosition.current;
|
const { left, top, visibleElement, visibleElementLeft, visibleElementTop } = scrollPosition.current;
|
||||||
|
|
||||||
if (!visibleElement) {
|
if (!visibleElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const entriesBeforeScrollPosition = entries.filter((entry) => {
|
||||||
|
if (!(entry.target instanceof HTMLElement)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPreloadPage = !entry.target.clientWidth && !entry.target.clientHeight;
|
||||||
|
if (isPreloadPage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isContinuousVerticalReadingModeActive) {
|
||||||
|
return entry.target.offsetTop < top;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.abs(entry.target.offsetLeft) < Math.abs(left);
|
||||||
|
});
|
||||||
|
|
||||||
|
const includesElementsBeforeScrollPosition = !!entriesBeforeScrollPosition.length;
|
||||||
|
if (!includesElementsBeforeScrollPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newLeft = left - visibleElementLeft + visibleElement.offsetLeft;
|
const newLeft = left - visibleElementLeft + visibleElement.offsetLeft;
|
||||||
const newTop = top - visibleElementTop + visibleElement.offsetTop;
|
const newTop = top - visibleElementTop + visibleElement.offsetTop;
|
||||||
|
|
||||||
@@ -140,11 +163,13 @@ const usePreserveOnReaderViewerElementMutation = (
|
|||||||
) =>
|
) =>
|
||||||
Array.from(nodes)
|
Array.from(nodes)
|
||||||
.filter((node) => node instanceof HTMLElement)
|
.filter((node) => node instanceof HTMLElement)
|
||||||
.flatMap((element) => {
|
.flatMap((element) =>
|
||||||
|
element instanceof HTMLImageElement ? [element] : Array.from(element.querySelectorAll('img')),
|
||||||
|
)
|
||||||
|
.forEach((element) => {
|
||||||
resizeAction(element);
|
resizeAction(element);
|
||||||
return Array.from(element.querySelectorAll('img'));
|
intersectionAction(element);
|
||||||
})
|
});
|
||||||
.forEach(intersectionAction);
|
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(preserveScrollPosition);
|
const resizeObserver = new ResizeObserver(preserveScrollPosition);
|
||||||
const intersectionObserver = new IntersectionObserver((entries) => {
|
const intersectionObserver = new IntersectionObserver((entries) => {
|
||||||
@@ -165,8 +190,8 @@ const usePreserveOnReaderViewerElementMutation = (
|
|||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
updateObservation(
|
updateObservation(
|
||||||
entry.addedNodes,
|
entry.addedNodes,
|
||||||
(n) => resizeObserver.observe(n),
|
(element) => resizeObserver.observe(element),
|
||||||
(n) => intersectionObserver.observe(n),
|
(element) => intersectionObserver.observe(element),
|
||||||
);
|
);
|
||||||
updateObservation(
|
updateObservation(
|
||||||
entry.removedNodes,
|
entry.removedNodes,
|
||||||
@@ -178,6 +203,7 @@ const usePreserveOnReaderViewerElementMutation = (
|
|||||||
|
|
||||||
mutationObserver.observe(scrollElement, {
|
mutationObserver.observe(scrollElement, {
|
||||||
childList: true,
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -185,7 +211,7 @@ const usePreserveOnReaderViewerElementMutation = (
|
|||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
intersectionObserver.disconnect();
|
intersectionObserver.disconnect();
|
||||||
};
|
};
|
||||||
}, [isContinuousReadingModeActive]);
|
}, [isContinuousReadingModeActive, isContinuousVerticalReadingModeActive]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useReaderPreserveScrollPosition = (
|
export const useReaderPreserveScrollPosition = (
|
||||||
@@ -193,11 +219,10 @@ export const useReaderPreserveScrollPosition = (
|
|||||||
pageIndex: number,
|
pageIndex: number,
|
||||||
readingMode: ReadingMode,
|
readingMode: ReadingMode,
|
||||||
readingDirection: ReadingDirection,
|
readingDirection: ReadingDirection,
|
||||||
isContinuousReadingModeActive: boolean,
|
|
||||||
setPageToScrollToIndex: ReaderStatePages['setPageToScrollToIndex'],
|
setPageToScrollToIndex: ReaderStatePages['setPageToScrollToIndex'],
|
||||||
pageScaleMode: ReaderPageScaleMode,
|
pageScaleMode: ReaderPageScaleMode,
|
||||||
) => {
|
) => {
|
||||||
usePreserveOnReaderViewerElementMutation(scrollElementRef, isContinuousReadingModeActive);
|
usePreserveOnLeadingPageRender(scrollElementRef, readingMode);
|
||||||
usePreserveOnWindowResize(readingMode, pageScaleMode, setPageToScrollToIndex, pageIndex);
|
usePreserveOnWindowResize(readingMode, pageScaleMode, setPageToScrollToIndex, pageIndex);
|
||||||
usePreserveOnValueChange(readingDirection, pageIndex, setPageToScrollToIndex);
|
usePreserveOnValueChange(readingDirection, pageIndex, setPageToScrollToIndex);
|
||||||
usePreserveOnValueChange(readingMode, pageIndex, setPageToScrollToIndex);
|
usePreserveOnValueChange(readingMode, pageIndex, setPageToScrollToIndex);
|
||||||
|
|||||||
Reference in New Issue
Block a user