Refine zoom viewer behavior

This commit is contained in:
Sam Becker 2025-02-09 19:40:37 -06:00
parent 33a430dcfd
commit 23eb87edd9
2 changed files with 33 additions and 30 deletions

View File

@ -6,7 +6,7 @@ import { RiCollapseDiagonalLine, RiExpandDiagonalLine } from 'react-icons/ri';
export type ZoomControlsRef = {
open: () => void
zoom: (zoomLevel?: number) => void
zoomTo: (zoomLevel?: number) => void
}
export default function ZoomControls({
@ -22,28 +22,33 @@ export default function ZoomControls({
}) {
const refContainer = useRef<HTMLDivElement>(null);
const { open, zoom, zoomLevel, isShown } = useImageZoomControls(
const {
open,
reset,
zoomTo,
zoomLevel,
viewerContainerRef,
} = useImageZoomControls(
refContainer,
isEnabled,
shouldZoomOnFKeydown,
);
useEffect(() => {
if (ref) { ref.current = { open, zoom }; }
}, [ref, open, zoom]);
if (ref) { ref.current = { open, zoomTo }; }
}, [ref, open, zoomTo]);
const shouldZoomTo2x = zoomLevel < 2;
const shouldZoomTo2x = zoomLevel !== 2;
const button =
<button
className={clsx(
isShown ? 'inline-flex' : 'hidden',
'fixed top-[20px] right-[70px] z-[100000]',
'fixed top-[20px] right-[70px]',
'size-10 items-center justify-center',
'rounded-full border-none',
'text-white bg-black/50 hover:bg-black/85',
)}
onClick={() => zoom(shouldZoomTo2x ? 2 : 1)}
onClick={() => shouldZoomTo2x ? zoomTo(2) : reset()}
>
{shouldZoomTo2x
? <RiCollapseDiagonalLine className="shrink-0" size={20} />
@ -56,9 +61,9 @@ export default function ZoomControls({
className={clsx('h-full', isEnabled && 'cursor-zoom-in')}
>
{children}
{typeof window !== 'undefined'
? createPortal(button, document.body)
: button}
{viewerContainerRef.current
? createPortal(button, viewerContainerRef.current)
: null}
</div>
);
}

View File

@ -11,10 +11,12 @@ export default function useImageZoomControls(
) {
const viewerRef = useRef<Viewer | null>(null);
const viewerContainerRef = useRef<HTMLDivElement>(null);
const { setShouldRespondToKeyboardCommands } = useAppState();
const [isShown, setIsShown] = useState(false);
const [zoomLevel, setZoomLevel] = useState(1);
const [colorLight, setColorLight] = useState<string>();
useMetaThemeColor({ colorLight });
@ -27,10 +29,15 @@ export default function useImageZoomControls(
viewerRef.current?.hide();
}, [viewerRef]);
const zoom = useCallback((zoomLevel = 1) => {
const zoomTo = useCallback((zoomLevel = 1) => {
viewerRef.current?.zoomTo(zoomLevel);
}, [viewerRef]);
const reset = useCallback(() => {
setZoomLevel(1);
viewerRef.current?.reset();
}, [viewerRef]);
// On 'F' keydown, toggle fullscreen
const handleKeyDown = useCallback(() => {
if (shouldExpandOnFKeydown) {
@ -41,8 +48,6 @@ export default function useImageZoomControls(
useEffect(() => {
if (imageRef.current && isEnabled) {
const closeButton = document
.getElementsByClassName('viewer-close')[0] as HTMLElement;
viewerRef.current = new Viewer(imageRef.current, {
navbar: false,
title: false,
@ -51,6 +56,10 @@ export default function useImageZoomControls(
reset: 2,
zoomOut: 3,
},
ready: ({ target }) => {
viewerContainerRef.current =
(target as any).viewer.viewer as HTMLDivElement;
},
url: (image: HTMLImageElement) => {
image.loading = 'eager';
return image.src;
@ -58,13 +67,10 @@ export default function useImageZoomControls(
show: () => {
setShouldRespondToKeyboardCommands?.(false);
setColorLight('#000');
setIsShown(true);
if (closeButton) { closeButton.style.display = 'none'; }
},
hide: () => {
setTimeout(() => {
setColorLight(undefined);
setIsShown(false);
}, 300);
},
hidden: () => {
@ -73,15 +79,6 @@ export default function useImageZoomControls(
zoom: ({ detail: { ratio } }) => {
setZoomLevel(ratio);
},
view: () => {
const container = document
.getElementsByClassName('viewer-container')[0];
if (container) {
const closeButton = document
.getElementsByClassName('viewer-close')[0] as HTMLElement;
if (closeButton) { closeButton.style.display = 'inline-flex'; }
}
},
});
return () => {
@ -92,15 +89,16 @@ export default function useImageZoomControls(
}, [
imageRef,
isEnabled,
zoom,
zoomTo,
setShouldRespondToKeyboardCommands,
]);
return {
open,
close,
zoom,
reset,
zoomTo,
zoomLevel,
isShown,
viewerContainerRef,
};
}