Created
August 11, 2024 14:04
-
-
Save lynnjinjie/a8a34ccd239c88940720e9614edacc87 to your computer and use it in GitHub Desktop.
react-pic-zoom
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 { cn } from '@/lib/utils' | |
| import { useRef, useState, type MouseEvent } from 'react' | |
| interface Props { | |
| url: string | |
| scale?: number | |
| } | |
| export default function PicZoom({ url, scale }: Props) { | |
| const [maskPosition, setMaskPosition] = useState<{ | |
| left: number | |
| top: number | |
| }>({ left: 0, top: 0 }) | |
| const [bigImgPosition, setBigImgPosition] = useState<{ | |
| left: number | |
| top: number | |
| }>({ left: 0, top: 0 }) | |
| const [isShowZoom, setIsShowZoom] = useState(false) | |
| const boxRef = useRef<HTMLDivElement | null>(null) | |
| const smallBoxRef = useRef<HTMLDivElement | null>(null) | |
| const maskBoxRef = useRef<HTMLDivElement | null>(null) | |
| const bigImgRef = useRef<HTMLImageElement | null>(null) | |
| const handleSmallBoxOver = (e: MouseEvent) => { | |
| setIsShowZoom(true) | |
| } | |
| const handleSmallBoxOut = (e: MouseEvent) => { | |
| setIsShowZoom(false) | |
| } | |
| const handleSmallBoxMove = (e: MouseEvent) => { | |
| const box = boxRef.current | |
| const smallBox = smallBoxRef.current | |
| const maskBox = maskBoxRef.current | |
| const bigImg = bigImgRef.current | |
| if (box && smallBox && maskBox && bigImg) { | |
| const { left, top } = box.getBoundingClientRect() | |
| let x = e.clientX - left - maskBox.offsetWidth / 2 | |
| let y = e.clientY - top - maskBox.offsetHeight / 2 | |
| x < 0 && (x = 0) | |
| y < 0 && (y = 0) | |
| let c = smallBox.offsetHeight / 2 | |
| y > c && (y = c) | |
| let l = smallBox.offsetWidth / 2 | |
| x > l && (x = l) | |
| setMaskPosition({ left: x, top: y }) | |
| let u = smallBox.offsetWidth / bigImg.offsetWidth | |
| let d = smallBox.offsetHeight / bigImg.offsetHeight | |
| setBigImgPosition({ left: -x / u, top: -y / d }) | |
| } | |
| } | |
| return ( | |
| <div ref={boxRef} className="relative w-[300px]"> | |
| <div | |
| ref={smallBoxRef} | |
| className="relative h-[300px] overflow-hidden text-center" | |
| onMouseOver={handleSmallBoxOver} | |
| onMouseMove={handleSmallBoxMove} | |
| onMouseOut={handleSmallBoxOut} | |
| > | |
| <img className="h-full w-auto object-contain" src={url} alt="image" /> | |
| <div | |
| ref={maskBoxRef} | |
| className={cn( | |
| 'bg-[#f5cda3] opacity-40 cursor-move size-[150px] absolute', | |
| isShowZoom ? 'block' : 'hidden' | |
| )} | |
| style={{ | |
| left: maskPosition.left, | |
| top: maskPosition.top, | |
| }} | |
| ></div> | |
| </div> | |
| <div | |
| className={cn( | |
| 'absolute left-[330px] top-0 size-[420px] border border-zinc-300 overflow-hidden', | |
| isShowZoom ? 'block' : 'hidden' | |
| )} | |
| > | |
| <img | |
| ref={bigImgRef} | |
| className="size-[800px] absolute object-contain max-w-none max-h-none" | |
| src={url} | |
| alt="big-image" | |
| style={{ | |
| left: bigImgPosition.left, | |
| top: bigImgPosition.top, | |
| }} | |
| /> | |
| </div> | |
| </div> | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment