Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions frontend/src/components/Media/ZoomableImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const ZoomableImage = forwardRef<ZoomableImageRef, ZoomableImageProps>(
zoomIn,
zoomOut,
reset,
isFitReady
} = useZoomTransform({ imagePath, rotation, resetSignal });

useImperativeHandle(
Expand Down Expand Up @@ -113,6 +114,8 @@ export const ZoomableImage = forwardRef<ZoomableImageRef, ZoomableImageProps>(
transform: `rotate(${rotation}deg)`,
transformOrigin: 'center center',
pointerEvents: 'none',
opacity: isFitReady ? 1 : 0,
transition: isFitReady ? 'opacity 120ms ease-out' : 'none',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}}
/>
</div>
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/components/Media/__tests__/ZoomableImage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,28 @@ describe('ZoomableImage controlled transform behavior', () => {
expect(image.style.height).toBe('');
});

test('keeps the image hidden until the fit transform is ready, then fades it in', () => {
renderZoomableImage();

const viewport = screen.getByTestId('zoom-viewport');
const image = screen.getByAltText('test image');

expect(image.style.opacity).toBe('0');
expect(image.style.transition).toBe('none');

mockElementRect(
viewport,
{ width: 500, height: 400, left: 0, top: 0 },
{ clientWidth: 500, clientHeight: 400 },
);
mockImageDimensions(image, { width: 1000, height: 800 });

fireEvent.load(image);

expect(image.style.opacity).toBe('1');
expect(image.style.transition).toBe('opacity 120ms ease-out');
});

test('starts from the new image fit transform after switching images', () => {
const { viewport, rerender } = setupScene({
viewportSize: { width: 500, height: 400 },
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/hooks/useZoomTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const useZoomTransform = ({
const [isOverflowing, setIsOverflowing] = useState(false);
const [isPanning, setIsPanning] = useState(false);
const [isButtonZoom, setIsButtonZoom] = useState(false);
const [isFitReady, setIsFitReady] = useState(false);

const setRawImageDimensions = useCallback((dimensions: Size | null) => {
rawDimensionsRef.current = dimensions;
Expand Down Expand Up @@ -196,6 +197,7 @@ export const useZoomTransform = ({
isFitInitializedRef.current = true;
hasUserInteractedRef.current = false;
fitRetryCountRef.current = 0;
setIsFitReady(true);
}

return didApply;
Expand Down Expand Up @@ -297,6 +299,7 @@ export const useZoomTransform = ({
useEffect(() => {
setIsOverflowing(false);
setRawImageDimensions(null);
setIsFitReady(false);
resetToFit();
}, [imagePath, resetToFit, setRawImageDimensions]);

Expand Down Expand Up @@ -556,5 +559,6 @@ export const useZoomTransform = ({
zoomIn,
zoomOut,
reset: resetToFit,
isFitReady,
};
};
Loading