Prevent blinking on page switch in double mode
This commit is contained in:
@@ -30,6 +30,8 @@ export function imageStyle(settings: IReaderSettings): CSSProperties {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const doublePageStyling: CSSProperties = {};
|
||||||
|
|
||||||
const continuesVerticalStyling: CSSProperties = {
|
const continuesVerticalStyling: CSSProperties = {
|
||||||
marginBottom: '15px',
|
marginBottom: '15px',
|
||||||
};
|
};
|
||||||
@@ -53,6 +55,7 @@ export function imageStyle(settings: IReaderSettings): CSSProperties {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...baseStyling,
|
...baseStyling,
|
||||||
|
...(isDoublePageReader ? doublePageStyling : undefined),
|
||||||
...(isHorizontal ? continuesHorizontalStyling : undefined),
|
...(isHorizontal ? continuesHorizontalStyling : undefined),
|
||||||
...(isVertical ? continuesVerticalStyling : undefined),
|
...(isVertical ? continuesVerticalStyling : undefined),
|
||||||
...(settings.fitPageToWindow && !isHorizontal ? fitToPageStyling : 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 isMobileWidth = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
|
|
||||||
const imgStyle = imageStyle(settings);
|
const imgStyle = imageStyle(settings);
|
||||||
const isDoublePageReader = ['DoubleRTL', 'DoubleLTR'].includes(settings.readerType);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -84,7 +86,6 @@ export const Page = forwardRef((props: IProps, ref: any) => {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
minWidth: isDoublePageReader ? '100%' : undefined,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SpinnerImage
|
<SpinnerImage
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { MouseEvent, useEffect, useRef, useState } from 'react';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { IReaderProps } from '@/typings';
|
import { IReaderProps } from '@/typings';
|
||||||
import { Page } from '@/components/reader/Page';
|
import { Page } from '@/components/reader/Page';
|
||||||
import { DoublePage } from '@/components/reader/DoublePage';
|
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
|
||||||
const isSpreadPage = (image: HTMLImageElement): boolean => {
|
const isSpreadPage = (image: HTMLImageElement): boolean => {
|
||||||
@@ -18,69 +17,53 @@ const isSpreadPage = (image: HTMLImageElement): boolean => {
|
|||||||
return aspectRatio < 1;
|
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) {
|
export function DoublePagedPager(props: IReaderProps) {
|
||||||
const { pages, settings, setCurPage, initialPage, curPage, chapter, nextChapter, prevChapter } = props;
|
const { pages, settings, setCurPage, initialPage, curPage, chapter, nextChapter, prevChapter } = props;
|
||||||
|
|
||||||
const selfRef = useRef<HTMLDivElement>(null);
|
const selfRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const pagesToDisplayStateRef = useRef<boolean[]>([]);
|
||||||
|
const pageToPrevSpreadPageRef = useRef<number[]>([]);
|
||||||
|
|
||||||
const [pagesToSpreadState, setPagesToSpreadState] = useState(Array(pages.length).fill(false));
|
const [pagesToSpreadState, setPagesToSpreadState] = useState(Array(pages.length).fill(false));
|
||||||
const [pagesLoadState, setPagesLoadState] = useState<boolean[]>(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() {
|
function nextPage() {
|
||||||
if (curPage < pages.length - 1) {
|
const setNextPage = (page: number) => setCurPage(page === -1 ? pages.length - 1 : page);
|
||||||
const nextCurPage = curPage + getPagesToDisplay();
|
|
||||||
setCurPage(nextCurPage >= pages.length ? pages.length - 1 : nextCurPage);
|
const isLastPageDisplayed = pagesToDisplayStateRef.current[pages.length - 1];
|
||||||
} else if (settings.loadNextOnEnding) {
|
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();
|
nextChapter();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const page = pagesToDisplayStateRef.current.findIndex((displayed, index) => !displayed && index > curPage);
|
||||||
|
setNextPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevPage() {
|
function prevPage() {
|
||||||
if (curPage > 0) {
|
const setPrevPage = (page: number) => setCurPage(Math.max(page, 0));
|
||||||
const nextCurPage = curPage - pagesToGoBack();
|
|
||||||
setCurPage(nextCurPage < 0 ? 0 : nextCurPage);
|
const isFirstPageDisplayed = pagesToDisplayStateRef.current[0];
|
||||||
} else {
|
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();
|
prevChapter();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const page = [...pagesToDisplayStateRef.current].slice(0, curPage).findLastIndex((displayed) => !displayed);
|
||||||
|
setPrevPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
function goLeft() {
|
function goLeft() {
|
||||||
@@ -136,16 +119,6 @@ export function DoublePagedPager(props: IReaderProps) {
|
|||||||
setCurPage(initialPage);
|
setCurPage(initialPage);
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
const imageRequests: [number, ReturnType<(typeof requestManager)['requestImage']>][] = pages.map((page) => [
|
const imageRequests: [number, ReturnType<(typeof requestManager)['requestImage']>][] = pages.map((page) => [
|
||||||
page.index,
|
page.index,
|
||||||
@@ -187,23 +160,61 @@ export function DoublePagedPager(props: IReaderProps) {
|
|||||||
height: 'auto',
|
height: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{getPagesToDisplay() === 2 ? (
|
{pages.map(({ index, src }) => {
|
||||||
<DoublePage
|
const prevSpreadPage = (() => {
|
||||||
key={curPage}
|
let currentIndexOfPrevSpreadPage = pageToPrevSpreadPageRef.current[curPage];
|
||||||
index={curPage}
|
|
||||||
image1src={pages[curPage].src}
|
if (currentIndexOfPrevSpreadPage === undefined) {
|
||||||
image2src={pages[curPage + 1].src}
|
currentIndexOfPrevSpreadPage = Math.max(pagesToSpreadState.lastIndexOf(true, curPage), 0);
|
||||||
settings={settings}
|
|
||||||
/>
|
const areAllPrevPagesLoaded = pagesLoadState.slice(0, curPage).every(Boolean);
|
||||||
) : (
|
if (areAllPrevPagesLoaded) {
|
||||||
<Page
|
pageToPrevSpreadPageRef.current[curPage] = currentIndexOfPrevSpreadPage;
|
||||||
key={curPage}
|
}
|
||||||
index={curPage}
|
}
|
||||||
src={pages[curPage].src}
|
|
||||||
onImageLoad={() => {}}
|
return currentIndexOfPrevSpreadPage > 0
|
||||||
settings={settings}
|
? 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={src}
|
||||||
|
index={index}
|
||||||
|
src={src}
|
||||||
|
onImageLoad={() => {}}
|
||||||
|
settings={settings}
|
||||||
|
display={displayPage}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user