forked from akai/readest
mobile: swipe up to toggle header/footer (#205)
This commit is contained in:
@@ -4,7 +4,7 @@ import { BookConfig } from '@/types/book';
|
||||
import { FoliateView, wrappedFoliateView } from '@/types/view';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useParallelViewStore } from '@/store/parallelViewStore';
|
||||
import { useClickEvent } from '../hooks/useClickEvent';
|
||||
import { useClickEvent, useTouchEvent } from '../hooks/useClickEvent';
|
||||
import { useFoliateEvents } from '../hooks/useFoliateEvents';
|
||||
import { useProgressSync } from '../hooks/useProgressSync';
|
||||
import { useAutoHideScrollbar } from '../hooks/useAutoHideScrollbar';
|
||||
@@ -17,6 +17,9 @@ import {
|
||||
handleMouseup,
|
||||
handleClick,
|
||||
handleWheel,
|
||||
handleTouchStart,
|
||||
handleTouchMove,
|
||||
handleTouchEnd,
|
||||
} from '../utils/iframeEventHandlers';
|
||||
|
||||
const FoliateViewer: React.FC<{
|
||||
@@ -42,7 +45,6 @@ const FoliateViewer: React.FC<{
|
||||
|
||||
const progressRelocateHandler = (event: Event) => {
|
||||
const detail = (event as CustomEvent).detail;
|
||||
// console.log('relocate:', detail);
|
||||
setProgress(bookKey, detail.cfi, detail.tocItem, detail.section, detail.location, detail.range);
|
||||
};
|
||||
|
||||
@@ -68,6 +70,9 @@ const FoliateViewer: React.FC<{
|
||||
detail.doc.addEventListener('mouseup', handleMouseup.bind(null, bookKey));
|
||||
detail.doc.addEventListener('click', handleClick.bind(null, bookKey));
|
||||
detail.doc.addEventListener('wheel', handleWheel.bind(null, bookKey));
|
||||
detail.doc.addEventListener('touchstart', handleTouchStart.bind(null, bookKey));
|
||||
detail.doc.addEventListener('touchmove', handleTouchMove.bind(null, bookKey));
|
||||
detail.doc.addEventListener('touchend', handleTouchEnd.bind(null, bookKey));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -88,6 +93,7 @@ const FoliateViewer: React.FC<{
|
||||
}
|
||||
};
|
||||
|
||||
useTouchEvent(bookKey, viewRef);
|
||||
const { handleTurnPage } = useClickEvent(bookKey, viewRef, containerRef);
|
||||
useFoliateEvents(viewRef.current, {
|
||||
onLoad: docLoadHandler,
|
||||
|
||||
@@ -31,7 +31,7 @@ export const useClickEvent = (
|
||||
const centerEndX = rect.left + rect.width * 0.625;
|
||||
if (screenX >= centerStartX && screenX <= centerEndX) {
|
||||
// toggle visibility of the header bar and the footer bar
|
||||
setHoveredBookKey(hoveredBookKey ? '' : bookKey);
|
||||
setHoveredBookKey(hoveredBookKey ? null : bookKey);
|
||||
} else if (screenX >= rect.left + rect.width / 2) {
|
||||
viewRef.current?.goRight();
|
||||
} else if (screenX < rect.left + rect.width / 2) {
|
||||
@@ -73,3 +73,82 @@ export const useClickEvent = (
|
||||
handleTurnPage,
|
||||
};
|
||||
};
|
||||
|
||||
interface IframeTouch {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
screenX: number;
|
||||
screenY: number;
|
||||
}
|
||||
|
||||
interface IframeTouchEvent {
|
||||
targetTouches: IframeTouch[];
|
||||
}
|
||||
|
||||
export const useTouchEvent = (
|
||||
bookKey: string,
|
||||
viewRef: React.MutableRefObject<FoliateView | null>,
|
||||
) => {
|
||||
const { hoveredBookKey, setHoveredBookKey } = useReaderStore();
|
||||
|
||||
let touchStart: IframeTouch | null = null;
|
||||
let touchEnd: IframeTouch | null = null;
|
||||
|
||||
const onTouchStart = (e: IframeTouchEvent) => {
|
||||
touchEnd = null;
|
||||
const touch = e.targetTouches[0];
|
||||
if (!touch) return;
|
||||
touchStart = touch;
|
||||
};
|
||||
|
||||
const onTouchMove = (e: IframeTouchEvent) => {
|
||||
if (!touchStart) return;
|
||||
const touch = e.targetTouches[0];
|
||||
if (touch) {
|
||||
touchEnd = touch;
|
||||
}
|
||||
};
|
||||
|
||||
const onTouchEnd = (e: IframeTouchEvent) => {
|
||||
if (!touchStart) return;
|
||||
|
||||
const touch = e.targetTouches[0];
|
||||
if (touch) {
|
||||
touchEnd = touch;
|
||||
};
|
||||
|
||||
const windowWidth = window.innerWidth;
|
||||
if (touchEnd) {
|
||||
const deltaY = touchEnd.screenY - touchStart.screenY;
|
||||
const deltaX = touchEnd.screenX - touchStart.screenX;
|
||||
// also check for deltaX to prevent swipe page turn from triggering the toggle
|
||||
if (deltaY < -10 && Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaX) < windowWidth * 0.3) {
|
||||
// swipe up to toggle the header bar and the footer bar
|
||||
setHoveredBookKey(hoveredBookKey ? null : bookKey);
|
||||
}
|
||||
}
|
||||
|
||||
touchStart = null;
|
||||
touchEnd = null;
|
||||
};
|
||||
|
||||
const handleTouch = (msg: MessageEvent) => {
|
||||
if (msg.data && msg.data.bookKey === bookKey) {
|
||||
if (msg.data.type === 'iframe-touchstart') {
|
||||
onTouchStart(msg.data);
|
||||
} else if (msg.data.type === 'iframe-touchmove') {
|
||||
onTouchMove(msg.data);
|
||||
} else if (msg.data.type === 'iframe-touchend') {
|
||||
onTouchEnd(msg.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', handleTouch);
|
||||
return () => {
|
||||
window.removeEventListener('message', handleTouch);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hoveredBookKey, viewRef]);
|
||||
}
|
||||
|
||||
@@ -130,3 +130,36 @@ export const handleClick = (bookKey: string, event: MouseEvent) => {
|
||||
}
|
||||
}, doubleClickThreshold);
|
||||
};
|
||||
|
||||
const handleTouchEv = (bookKey: string, event: TouchEvent, type: string) => {
|
||||
const touch = event.targetTouches[0];
|
||||
const touches = [];
|
||||
if (touch) {
|
||||
touches.push({
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY,
|
||||
screenX: touch.screenX,
|
||||
screenY: touch.screenY,
|
||||
});
|
||||
}
|
||||
window.postMessage(
|
||||
{
|
||||
type: type,
|
||||
bookKey,
|
||||
targetTouches: touches,
|
||||
},
|
||||
'*',
|
||||
);
|
||||
}
|
||||
|
||||
export const handleTouchStart = (bookKey: string, event: TouchEvent) => {
|
||||
handleTouchEv(bookKey, event, 'iframe-touchstart');
|
||||
};
|
||||
|
||||
export const handleTouchMove = (bookKey: string, event: TouchEvent) => {
|
||||
handleTouchEv(bookKey, event, 'iframe-touchmove');
|
||||
};
|
||||
|
||||
export const handleTouchEnd = (bookKey: string, event: TouchEvent) => {
|
||||
handleTouchEv(bookKey, event, 'iframe-touchend');
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ interface ReaderStore {
|
||||
bookKeys: string[];
|
||||
hoveredBookKey: string | null;
|
||||
setBookKeys: (keys: string[]) => void;
|
||||
setHoveredBookKey: (key: string) => void;
|
||||
setHoveredBookKey: (key: string | null) => void;
|
||||
setBookmarkRibbonVisibility: (key: string, visible: boolean) => void;
|
||||
|
||||
setProgress: (
|
||||
@@ -59,7 +59,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
bookKeys: [],
|
||||
hoveredBookKey: null,
|
||||
setBookKeys: (keys: string[]) => set({ bookKeys: keys }),
|
||||
setHoveredBookKey: (key: string) => set({ hoveredBookKey: key }),
|
||||
setHoveredBookKey: (key: string | null) => set({ hoveredBookKey: key }),
|
||||
|
||||
getView: (key: string | null) => (key && get().viewStates[key]?.view) || null,
|
||||
setView: (key: string, view) =>
|
||||
|
||||
Reference in New Issue
Block a user