import { useEffect, useRef, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import { MdOutlineChevronLeft, MdOutlineChevronRight, MdOutlineClose, } from "react-icons/md"; import { network } from "../network/network.ts"; import Badge from "./badge.tsx"; export default function Gallery({ images, nsfw, }: { images: number[]; nsfw: number[]; }) { const [currentIndex, setCurrentIndex] = useState(0); const [direction, setDirection] = useState(0); // 方向:1=向右,-1=向左 const [isHovered, setIsHovered] = useState(false); const [width, setWidth] = useState(0); const containerRef = useRef(null); const dialogRef = useRef(null); useEffect(() => { const updateWidth = () => { if (containerRef.current) { setWidth(containerRef.current.clientWidth); } }; updateWidth(); window.addEventListener("resize", updateWidth); return () => { window.removeEventListener("resize", updateWidth); }; }, []); // 预加载下一张图片 useEffect(() => { if (!images || images.length <= 1) return; const nextIndex = (currentIndex + 1) % images.length; const nextImageUrl = network.getImageUrl(images[nextIndex]); const img = new Image(); img.src = nextImageUrl; }, [currentIndex, images]); if (!images || images.length === 0) { return <>; } const goToPrevious = () => { setDirection(-1); setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1)); }; const goToNext = () => { setDirection(1); setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1)); }; const goToIndex = (index: number) => { setDirection(index > currentIndex ? 1 : -1); setCurrentIndex(index); }; if (nsfw == null) { nsfw = []; } // 如果图片数量超过8张,显示数字而不是圆点 const showDots = images.length <= 8; return ( <>
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > {/* 图片区域 */}
{ dialogRef.current?.showModal(); }} > {width > 0 && ( ({ x: dir > 0 ? width : -width, }), center: { x: 0, transition: { duration: 0.3, ease: "linear" }, }, exit: (dir: number) => ({ x: dir > 0 ? -width : width, transition: { duration: 0.3, ease: "linear" }, }), }} initial="enter" animate="center" exit="exit" custom={direction} > )}
{/* 左右按钮 */} {images.length > 1 && ( <> )} {/* 底部指示器 */} {images.length > 1 && (
{showDots ? ( /* 圆点指示器 */
{images.map((_, index) => (
) : ( /* 数字指示器 */
{currentIndex + 1} / {images.length}
)}
)}
); } function GalleryFullscreen({ dialogRef, images, nsfw, currentIndex, direction, goToPrevious, goToNext, setDirection, setCurrentIndex, }: { dialogRef: React.RefObject; images: number[]; nsfw: number[]; currentIndex: number; direction: number; goToPrevious: () => void; goToNext: () => void; setDirection: (direction: number) => void; setCurrentIndex: (index: number) => void; }) { const [width, setWidth] = useState(0); const containerRef = useRef(null); const thumbnailContainerRef = useRef(null); const hideTimeoutRef = useRef(null); const [isHovered, setIsHovered] = useState(true); useEffect(() => { const updateWidth = () => { if (containerRef.current) { console.log(containerRef.current.clientWidth); setWidth(containerRef.current.clientWidth); } }; updateWidth(); window.addEventListener("resize", updateWidth); return () => { window.removeEventListener("resize", updateWidth); }; }, []); useEffect(() => { const handleMouseMove = () => { setIsHovered(true); if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); } hideTimeoutRef.current = setTimeout(() => { setIsHovered(false); }, 2000); }; if (dialogRef.current?.open) { window.addEventListener("mousemove", handleMouseMove); window.addEventListener("touchstart", handleMouseMove); } return () => { window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("touchstart", handleMouseMove); if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); } }; }, [dialogRef.current?.open, setIsHovered]); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (dialogRef.current?.open) { if (e.key === "ArrowLeft") { e.preventDefault(); goToPrevious(); } else if (e.key === "ArrowRight") { e.preventDefault(); goToNext(); } else if (e.key === "Escape") { dialogRef.current?.close(); } } }; window.addEventListener("keydown", handleKeyDown); return () => { window.removeEventListener("keydown", handleKeyDown); }; }, [dialogRef, goToPrevious, goToNext]); useEffect(() => { if (thumbnailContainerRef.current && dialogRef.current?.open) { const thumbnail = thumbnailContainerRef.current.children[currentIndex] as HTMLElement; if (thumbnail) { thumbnail.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); } } }, [currentIndex, dialogRef]); return ( { dialogRef.current?.close(); }} className="modal" onMouseLeave={() => setIsHovered(false)} >
{width > 0 && ( ({ x: dir > 0 ? width : -width, }), center: { x: 0, transition: { duration: 0.3, ease: "linear" }, }, exit: (dir: number) => ({ x: dir > 0 ? -width : width, transition: { duration: 0.3, ease: "linear" }, }), }} initial="enter" animate="center" exit="exit" custom={direction} > )} {/* 全屏模式下的左右切换按钮 */} {images.length > 1 && ( <> {/* 图片缩略图列表 */}
e.stopPropagation()} >
{images.map((imageId, index) => ( ))}
{/* 关闭按钮 */} )}
); } function GalleryImage({ src, nfsw }: { src: string; nfsw: boolean }) { const [show, setShow] = useState(!nfsw); return (
{!show && ( <>
{ setShow(true); event.stopPropagation(); }} />
NSFW
)}
); }