Created
November 2, 2023 09:49
-
-
Save jackbkennedy/ecf37762a527ba4c00d48894351dfeff to your computer and use it in GitHub Desktop.
A react image component that focuses an image in the middle of the page and zooms in to make it easier to see.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useEffect, useRef, useState } from 'react'; | |
import ReactDOM from 'react-dom'; | |
type ZoomableImageProps = { | |
url: string; | |
style?: React.CSSProperties; // for custom styles | |
className?: string; // to allow custom classes | |
}; | |
/* | |
* A component that displays an image and allows it to be zoomed in/out | |
* by clicking on it. | |
*/ | |
export const ZoomableImageComponent: React.FC<ZoomableImageProps> = ({ | |
url, | |
style, | |
className, | |
}) => { | |
const [isZoomed, setIsZoomed] = useState(false); | |
const imageRef = useRef<HTMLImageElement>(null); | |
const handleImageClick = (e: React.MouseEvent) => { | |
e.stopPropagation(); | |
setIsZoomed(!isZoomed); | |
}; | |
useEffect(() => { | |
const handleDocumentClick = () => { | |
setIsZoomed(false); | |
}; | |
if (isZoomed) { | |
document.addEventListener('click', handleDocumentClick); | |
} | |
return () => { | |
document.removeEventListener('click', handleDocumentClick); | |
}; | |
}, [isZoomed]); | |
const zoomedImage = ( | |
<> | |
<div | |
style={{ | |
position: 'fixed', | |
top: 0, | |
left: 0, | |
width: '100%', | |
height: '100%', | |
backgroundColor: 'rgba(0, 0, 0, 0.5)', | |
zIndex: 999998, | |
}} | |
/> | |
<img | |
src={url} | |
alt="Zoomed" | |
style={{ | |
position: 'fixed', | |
top: '50%', | |
left: '50%', | |
transform: 'translate(-50%, -50%) scale(2)', | |
maxWidth: '35%', | |
maxHeight: '35%', | |
zIndex: 999999, | |
cursor: 'zoom-out', | |
}} | |
/> | |
</> | |
); | |
// Default styles that can be overridden by passing a `style` prop | |
const defaultStyles = { | |
width: '580px', | |
height: '420px', | |
borderRadius: '6px', | |
position: 'relative', | |
...style, // spread in additional styles if provided | |
}; | |
return ( | |
<div style={defaultStyles} className={className}> | |
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}> | |
<button onClick={handleImageClick} style={{ background: 'none', border: 'none', padding: 0 }}> | |
<img | |
ref={imageRef} | |
src={url} | |
alt="Thumbnail" | |
style={{ | |
maxWidth: '100%', | |
maxHeight: '100%', | |
borderRadius: '6px', | |
cursor: 'zoom-in', | |
transition: 'transform 0.3s', | |
zIndex: isZoomed ? 999999 : 1, | |
}} | |
/> | |
</button> | |
</div> | |
{isZoomed && ReactDOM.createPortal(zoomedImage, document.body)} | |
</div> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment