import React, { useState, useRef, useCallback, useEffect } from 'react' import { X } from 'lucide-react' import { createPortal } from 'react-dom' import './ImagePreview.scss' interface ImagePreviewProps { src: string onClose: () => void } export const ImagePreview: React.FC = ({ src, onClose }) => { const [scale, setScale] = useState(1) const [position, setPosition] = useState({ x: 0, y: 0 }) const [isDragging, setIsDragging] = useState(false) const dragStart = useRef({ x: 0, y: 0 }) const positionStart = useRef({ x: 0, y: 0 }) const containerRef = useRef(null) // 滚轮缩放 const handleWheel = useCallback((e: React.WheelEvent) => { e.preventDefault() const delta = e.deltaY > 0 ? 0.9 : 1.1 setScale(prev => Math.min(Math.max(prev * delta, 0.5), 5)) }, []) // 开始拖动 const handleMouseDown = useCallback((e: React.MouseEvent) => { if (scale <= 1) return e.preventDefault() setIsDragging(true) dragStart.current = { x: e.clientX, y: e.clientY } positionStart.current = { ...position } }, [scale, position]) // 拖动中 const handleMouseMove = useCallback((e: React.MouseEvent) => { if (!isDragging) return const dx = e.clientX - dragStart.current.x const dy = e.clientY - dragStart.current.y setPosition({ x: positionStart.current.x + dx, y: positionStart.current.y + dy }) }, [isDragging]) // 结束拖动 const handleMouseUp = useCallback(() => { setIsDragging(false) }, []) // 双击重置 const handleDoubleClick = useCallback(() => { setScale(1) setPosition({ x: 0, y: 0 }) }, []) // 点击背景关闭 const handleOverlayClick = useCallback((e: React.MouseEvent) => { if (e.target === containerRef.current) { onClose() } }, [onClose]) // ESC 关闭 useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [onClose]) return createPortal(
图片预览 1 ? (isDragging ? 'grabbing' : 'grab') : 'default' }} onWheel={handleWheel} onMouseDown={handleMouseDown} onDoubleClick={handleDoubleClick} draggable={false} />
, document.body ) }