feat: swipe to paginate on fixed layout books, closes #1028 (#1995)

This commit is contained in:
Huang Xin
2025-09-07 22:14:34 +08:00
committed by GitHub
parent 71c2143cba
commit e03b0af58a
3 changed files with 37 additions and 4 deletions
@@ -222,7 +222,7 @@ const FoliateViewer: React.FC<{
const { handlePageFlip, handleContinuousScroll } = usePagination(bookKey, viewRef, containerRef);
const mouseHandlers = useMouseEvent(bookKey, handlePageFlip, handleContinuousScroll);
const touchHandlers = useTouchEvent(bookKey, handleContinuousScroll);
const touchHandlers = useTouchEvent(bookKey, handlePageFlip, handleContinuousScroll);
useFoliateEvents(viewRef.current, {
onLoad: docLoadHandler,
@@ -58,6 +58,7 @@ interface IframeTouchEvent {
export const useTouchEvent = (
bookKey: string,
handlePageFlip: (msg: CustomEvent) => void,
handleContinuousScroll: (source: ScrollSource, delta: number, threshold: number) => void,
) => {
const { hoveredBookKey, setHoveredBookKey, getViewSettings } = useReaderStore();
@@ -120,6 +121,18 @@ export const useTouchEvent = (
setHoveredBookKey(null);
}
}
handlePageFlip(
new CustomEvent('touch-swipe', {
detail: {
deltaX,
deltaY,
startX: touchStart.screenX,
startY: touchStart.screenY,
endX: touchEnd.screenX,
endY: touchEnd.screenY,
},
}),
);
handleContinuousScroll('touch', deltaY, 30);
}
@@ -3,6 +3,7 @@ import { useEnv } from '@/context/EnvContext';
import { FoliateView } from '@/types/view';
import { ViewSettings } from '@/types/book';
import { useReaderStore } from '@/store/readerStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useDeviceControlStore } from '@/store/deviceStore';
import { eventDispatcher } from '@/utils/event';
import { isTauriAppPlatform } from '@/services/environment';
@@ -38,6 +39,7 @@ export const usePagination = (
containerRef: React.RefObject<HTMLDivElement>,
) => {
const { appService } = useEnv();
const { getBookData } = useBookDataStore();
const { getViewSettings, getViewState } = useReaderStore();
const { hoveredBookKey, setHoveredBookKey } = useReaderStore();
const { acquireVolumeKeyInterception, releaseVolumeKeyInterception } = useDeviceControlStore();
@@ -46,7 +48,8 @@ export const usePagination = (
msg: MessageEvent | CustomEvent | React.MouseEvent<HTMLDivElement, MouseEvent>,
) => {
const viewState = getViewState(bookKey);
if (!viewState?.inited) return;
const bookData = getBookData(bookKey);
if (!viewState?.inited || !bookData) return;
if (msg instanceof MessageEvent) {
if (msg.data && msg.data.bookKey === bookKey) {
@@ -121,15 +124,28 @@ export const usePagination = (
}
}
} else if (msg instanceof CustomEvent) {
const { keyName } = msg.detail;
const viewSettings = getViewSettings(bookKey);
if (viewSettings?.volumeKeysToFlip) {
if (msg.type === 'native-key-down' && viewSettings?.volumeKeysToFlip) {
const { keyName } = msg.detail;
setHoveredBookKey('');
if (keyName === 'VolumeUp') {
viewPagination(viewRef.current, viewSettings, 'left');
} else if (keyName === 'VolumeDown') {
viewPagination(viewRef.current, viewSettings, 'right');
}
} else if (
msg.type === 'touch-swipe' &&
bookData.bookDoc?.rendition?.layout === 'pre-paginated' &&
viewSettings?.zoomLevel === 100
) {
const { deltaX, deltaY } = msg.detail;
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 30) {
if (deltaX > 0) {
viewPagination(viewRef.current, viewSettings, 'left');
} else {
viewPagination(viewRef.current, viewSettings, 'right');
}
}
}
} else {
if (msg.type === 'click') {
@@ -150,6 +166,10 @@ export const usePagination = (
const handleContinuousScroll = (mode: ScrollSource, scrollDelta: number, threshold: number) => {
const renderer = viewRef.current?.renderer;
const viewSettings = getViewSettings(bookKey)!;
const bookData = getBookData(bookKey)!;
// Currently continuous scroll is not supported in pre-paginated layout
if (bookData.bookDoc?.rendition?.layout === 'pre-paginated') return;
if (renderer && viewSettings.scrolled && viewSettings.continuousScroll) {
const doScroll = () => {
// may have overscroll where the start is greater than 0