Prevent blinking on page switch in double mode

This commit is contained in:
schroda
2024-09-16 22:24:08 +02:00
parent fe3bbedde9
commit 83ce1caee6
2 changed files with 89 additions and 77 deletions

View File

@@ -30,6 +30,8 @@ export function imageStyle(settings: IReaderSettings): CSSProperties {
objectFit: 'contain',
};
const doublePageStyling: CSSProperties = {};
const continuesVerticalStyling: CSSProperties = {
marginBottom: '15px',
};
@@ -53,6 +55,7 @@ export function imageStyle(settings: IReaderSettings): CSSProperties {
return {
...baseStyling,
...(isDoublePageReader ? doublePageStyling : undefined),
...(isHorizontal ? continuesHorizontalStyling : undefined),
...(isVertical ? continuesVerticalStyling : undefined),
...(settings.fitPageToWindow && !isHorizontal ? fitToPageStyling : undefined),
@@ -74,7 +77,6 @@ export const Page = forwardRef((props: IProps, ref: any) => {
const isMobileWidth = useMediaQuery(theme.breakpoints.down('md'));
const imgStyle = imageStyle(settings);
const isDoublePageReader = ['DoubleRTL', 'DoubleLTR'].includes(settings.readerType);
return (
<Box
@@ -84,7 +86,6 @@ export const Page = forwardRef((props: IProps, ref: any) => {
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minWidth: isDoublePageReader ? '100%' : undefined,
}}
>
<SpinnerImage

View File

@@ -10,7 +10,6 @@ import { MouseEvent, useEffect, useRef, useState } from 'react';
import Box from '@mui/material/Box';
import { IReaderProps } from '@/typings';
import { Page } from '@/components/reader/Page';
import { DoublePage } from '@/components/reader/DoublePage';
import { requestManager } from '@/lib/requests/RequestManager.ts';
const isSpreadPage = (image: HTMLImageElement): boolean => {
@@ -18,69 +17,53 @@ const isSpreadPage = (image: HTMLImageElement): boolean => {
return aspectRatio < 1;
};
const isSinglePage = (index: number, spreadPages: boolean[], offsetFirstPage: boolean): boolean => {
// Page is single if it is spread page
if (spreadPages[index] || spreadPages[index + 1]) return true;
// Page is single if it is last page
if (index === spreadPages.length - 1) return true;
// Page is single if number of single pages since last spread is odd
const previousSpreadIndex = spreadPages.lastIndexOf(true, index - 1);
const numberOfNonSpreads = index - (previousSpreadIndex + 1);
return offsetFirstPage ? numberOfNonSpreads % 2 === 0 : numberOfNonSpreads % 2 === 1;
};
export function DoublePagedPager(props: IReaderProps) {
const { pages, settings, setCurPage, initialPage, curPage, chapter, nextChapter, prevChapter } = props;
const selfRef = useRef<HTMLDivElement>(null);
const pagesToDisplayStateRef = useRef<boolean[]>([]);
const pageToPrevSpreadPageRef = useRef<number[]>([]);
const [pagesToSpreadState, setPagesToSpreadState] = useState(Array(pages.length).fill(false));
const [pagesLoadState, setPagesLoadState] = useState<boolean[]>(Array(pages.length).fill(false));
function getPagesToDisplay(): number {
let pagesToDisplay = 1; // has to be at least one so skipping forward while pages are still loading is possible
if (curPage < pages.length) {
if (pagesLoadState[curPage]) {
pagesToDisplay = 1;
if (pagesToSpreadState[curPage]) return pagesToDisplay;
}
}
if (curPage + 1 < pages.length) {
if (pagesLoadState[curPage + 1]) {
if (isSinglePage(curPage, pagesToSpreadState, settings.offsetFirstPage)) return pagesToDisplay;
pagesToDisplay = 2;
}
}
return pagesToDisplay;
}
function pagesToGoBack() {
// If previous page is single page, go only one page pack
if (isSinglePage(curPage - 2, pagesToSpreadState, settings.offsetFirstPage)) {
return 1;
}
// Otherwise go two pages back
return 2;
}
function nextPage() {
if (curPage < pages.length - 1) {
const nextCurPage = curPage + getPagesToDisplay();
setCurPage(nextCurPage >= pages.length ? pages.length - 1 : nextCurPage);
} else if (settings.loadNextOnEnding) {
nextChapter();
const setNextPage = (page: number) => setCurPage(page === -1 ? pages.length - 1 : page);
const isLastPageDisplayed = pagesToDisplayStateRef.current[pages.length - 1];
if (isLastPageDisplayed && settings.loadNextOnEnding) {
// make sure to set last page as current page so that the chapter gets marked as read before opening the next chapter
const isLastPage = curPage === pages.length - 1;
if (!isLastPage) {
setNextPage(pages.length - 1);
}
nextChapter();
return;
}
const page = pagesToDisplayStateRef.current.findIndex((displayed, index) => !displayed && index > curPage);
setNextPage(page);
}
function prevPage() {
if (curPage > 0) {
const nextCurPage = curPage - pagesToGoBack();
setCurPage(nextCurPage < 0 ? 0 : nextCurPage);
} else {
prevChapter();
const setPrevPage = (page: number) => setCurPage(Math.max(page, 0));
const isFirstPageDisplayed = pagesToDisplayStateRef.current[0];
if (isFirstPageDisplayed) {
// not important, but for consistency make sure that first page gets set as current page in case it was displayed
const isFirstPage = !curPage;
if (!isFirstPage) {
setPrevPage(0);
}
prevChapter();
return;
}
const page = [...pagesToDisplayStateRef.current].slice(0, curPage).findLastIndex((displayed) => !displayed);
setPrevPage(page);
}
function goLeft() {
@@ -136,16 +119,6 @@ export function DoublePagedPager(props: IReaderProps) {
setCurPage(initialPage);
}, [initialPage]);
useEffect(() => {
if (settings.offsetFirstPage) {
if (getPagesToDisplay() === 2) {
setCurPage(curPage + 1);
}
} else if (curPage > 0 && !isSinglePage(curPage - 1, pagesToSpreadState, settings.offsetFirstPage)) {
setCurPage(curPage - 1);
}
}, [settings.offsetFirstPage]);
useEffect(() => {
const imageRequests: [number, ReturnType<(typeof requestManager)['requestImage']>][] = pages.map((page) => [
page.index,
@@ -187,23 +160,61 @@ export function DoublePagedPager(props: IReaderProps) {
height: 'auto',
}}
>
{getPagesToDisplay() === 2 ? (
<DoublePage
key={curPage}
index={curPage}
image1src={pages[curPage].src}
image2src={pages[curPage + 1].src}
settings={settings}
/>
) : (
{pages.map(({ index, src }) => {
const prevSpreadPage = (() => {
let currentIndexOfPrevSpreadPage = pageToPrevSpreadPageRef.current[curPage];
if (currentIndexOfPrevSpreadPage === undefined) {
currentIndexOfPrevSpreadPage = Math.max(pagesToSpreadState.lastIndexOf(true, curPage), 0);
const areAllPrevPagesLoaded = pagesLoadState.slice(0, curPage).every(Boolean);
if (areAllPrevPagesLoaded) {
pageToPrevSpreadPageRef.current[curPage] = currentIndexOfPrevSpreadPage;
}
}
return currentIndexOfPrevSpreadPage > 0
? currentIndexOfPrevSpreadPage + 1
: currentIndexOfPrevSpreadPage;
})();
// index of first page after a spread page will be 0
const normalizedCurPageIndexForSpreadPages = curPage - prevSpreadPage;
const isFirstPage = curPage === 0;
// only offset pages before the first spread page, after the first spread page handle as if "offsetFirstPage" is disabled
const firstPageOffset =
Number(settings.offsetFirstPage) +
Number(settings.offsetFirstPage && !normalizedCurPageIndexForSpreadPages && !isFirstPage);
const normalizedCurPageIndex = normalizedCurPageIndexForSpreadPages - firstPageOffset;
const isCurPageEven = !(normalizedCurPageIndex % 2);
const secondPage = curPage + (isCurPageEven ? 1 : -1);
const isCurPage = index === curPage;
const isSecondPage = index === secondPage;
const isCurrentPageSpreadPage = pagesToSpreadState[curPage];
const isSecondPageSpreadPage = pagesToSpreadState[secondPage];
const hasSpreadPage = isCurrentPageSpreadPage || isSecondPageSpreadPage;
const displaySecondPage = isSecondPage && !hasSpreadPage;
const displayPage = isCurPage || displaySecondPage;
pagesToDisplayStateRef.current[index] = displayPage;
return (
<Page
key={curPage}
index={curPage}
src={pages[curPage].src}
key={src}
index={index}
src={src}
onImageLoad={() => {}}
settings={settings}
display={displayPage}
/>
)}
);
})}
</Box>
</Box>
);