diff --git a/src/components/utils/ZoomImage.tsx b/src/components/utils/ZoomImage.tsx index 4999bdeef..73d0b26cd 100644 --- a/src/components/utils/ZoomImage.tsx +++ b/src/components/utils/ZoomImage.tsx @@ -24,6 +24,7 @@ export function ZoomImage( const [zoomable, setZoomable] = React.useState(false); const [active, setActive] = React.useState(false); const [opened, setOpened] = React.useState(false); + const [placeholderRect, setPlaceholderRect] = React.useState(null); // Only allow zooming when image will not actually be larger and on mobile React.useEffect(() => { @@ -36,10 +37,17 @@ export function ZoomImage( const mediaQueryList = window.matchMedia('(min-width: 768px)'); const resizeObserver = - width && typeof ResizeObserver !== 'undefined' + typeof ResizeObserver !== 'undefined' ? new ResizeObserver((entries) => { - viewWidth = entries[0]?.contentRect.width; - onChange(); + const imgEntry = entries[0]; + + // Since the image is removed from the DOM when the modal is opened, + // We only care when the size is defined. + if (imgEntry && imgEntry.contentRect.width !== 0) { + viewWidth = entries[0]?.contentRect.width; + setPlaceholderRect(entries[0].contentRect); + onChange(); + } }) : null; @@ -104,11 +112,25 @@ export function ZoomImage( return ( <> {opened ? ( - ReactDOM.createPortal( - , - document.body, - ) + <> + {placeholderRect ? ( + // Placeholder to keep the layout stable when the image is removed from the DOM + + ) : null} + + {ReactDOM.createPortal( + , + document.body, + )} + ) : ( + // When zooming, remove the image from the DOM to let the browser animates it with View Transition.