feat: add zoom functionality to BookReader and related components, enabling zoom state management and touch gesture handling

This commit is contained in:
Julien Froidefond
2025-10-16 14:38:31 +02:00
parent 0716f38175
commit 4209557768
4 changed files with 28 additions and 12 deletions

View File

@@ -19,6 +19,7 @@ export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps)
const [isDoublePage, setIsDoublePage] = useState(false);
const [showControls, setShowControls] = useState(false);
const [showThumbnails, setShowThumbnails] = useState(false);
const [isZoomed, setIsZoomed] = useState(false);
const readerRef = useRef<HTMLDivElement>(null);
const isLandscape = useOrientation();
const { direction, toggleDirection, isRTL } = useReadingDirection();
@@ -43,6 +44,7 @@ export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps)
onClose,
direction,
nextBook,
isZoomed,
});
const { preloadPage, getPageUrl, cleanCache } = usePageCache({
@@ -138,6 +140,7 @@ export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps)
shouldShowDoublePage={shouldShowDoublePage}
isRTL={isRTL}
onThumbnailLoad={handleThumbnailLoad}
onZoomChange={setIsZoomed}
/>
<NavigationBar

View File

@@ -10,6 +10,7 @@ interface ReaderContentProps {
shouldShowDoublePage: (page: number) => boolean;
isRTL: boolean;
onThumbnailLoad: (pageNumber: number) => void;
onZoomChange?: (isZoomed: boolean) => void;
}
export const ReaderContent = ({
@@ -22,6 +23,7 @@ export const ReaderContent = ({
shouldShowDoublePage,
isRTL,
onThumbnailLoad,
onZoomChange,
}: ReaderContentProps) => {
return (
<div className="relative flex-1 flex items-center justify-center overflow-hidden p-1">
@@ -34,6 +36,7 @@ export const ReaderContent = ({
isDoublePage={isDoublePage}
isRTL={isRTL}
order="first"
onZoomChange={onZoomChange}
/>
{isDoublePage && shouldShowDoublePage(currentPage) && (
@@ -45,6 +48,7 @@ export const ReaderContent = ({
isDoublePage={true}
isRTL={isRTL}
order="second"
onZoomChange={onZoomChange}
/>
)}
</div>

View File

@@ -1,5 +1,6 @@
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import { cn } from "@/lib/utils";
import { useState } from "react";
interface ZoomablePageProps {
pageUrl: string | null;
@@ -9,6 +10,7 @@ interface ZoomablePageProps {
isDoublePage?: boolean;
isRTL?: boolean;
order?: "first" | "second";
onZoomChange?: (isZoomed: boolean) => void;
}
export const ZoomablePage = ({
@@ -19,7 +21,14 @@ export const ZoomablePage = ({
isDoublePage = false,
isRTL = false,
order = "first",
onZoomChange,
}: ZoomablePageProps) => {
const [currentScale, setCurrentScale] = useState(1);
const handleTransform = (ref: any, state: { scale: number; positionX: number; positionY: number }) => {
setCurrentScale(state.scale);
onZoomChange?.(state.scale > 1.1);
};
return (
<div
className={cn(
@@ -55,6 +64,7 @@ export const ZoomablePage = ({
panning={{ disabled: false }}
limitToBounds={true}
centerZoomedOut={false}
onTransformed={handleTransform}
>
<TransformComponent
wrapperClass="w-full h-full flex items-center justify-center"

View File

@@ -10,6 +10,7 @@ interface UsePageNavigationProps {
onClose?: (currentPage: number) => void;
direction: "ltr" | "rtl";
nextBook?: KomgaBook | null;
isZoomed?: boolean;
}
export const usePageNavigation = ({
@@ -19,6 +20,7 @@ export const usePageNavigation = ({
onClose,
direction,
nextBook,
isZoomed = false,
}: UsePageNavigationProps) => {
const router = useRouter();
const cPage = ClientOfflineBookService.getCurrentPage(book);
@@ -133,25 +135,22 @@ export const usePageNavigation = ({
const handleTouchStart = useCallback(
(event: TouchEvent) => {
// Ne gérer que les gestes à un seul doigt
if (event.touches.length === 1) {
touchStartXRef.current = event.touches[0].clientX;
touchStartYRef.current = event.touches[0].clientY;
currentPageRef.current = currentPage;
} else {
// Reset les références pour les gestes multi-touch (pinch)
// Si on est zoomé, on ne gère pas la navigation
if (isZoomed) {
touchStartXRef.current = null;
touchStartYRef.current = null;
return;
}
touchStartXRef.current = event.touches[0].clientX;
touchStartYRef.current = event.touches[0].clientY;
currentPageRef.current = currentPage;
},
[currentPage]
[currentPage, isZoomed]
);
const handleTouchEnd = useCallback(
(event: TouchEvent) => {
// Ne traiter que les gestes à un seul doigt
if (event.touches.length > 1) return;
if (touchStartXRef.current === null || touchStartYRef.current === null) return;
const touchEndX = event.changedTouches[0].clientX;
@@ -163,7 +162,7 @@ export const usePageNavigation = ({
// on ne fait rien (pour éviter de confondre avec un scroll)
if (Math.abs(deltaY) > Math.abs(deltaX)) return;
// Augmenter le seuil pour éviter les changements de page accidentels
// Seuil pour éviter les changements de page accidentels
if (Math.abs(deltaX) > 100) {
if (deltaX > 0) {
// Swipe vers la droite