feat: enhance GalleryFullscreen with thumbnail navigation and hover effects

This commit is contained in:
2025-11-27 22:04:59 +08:00
parent 5febba690b
commit dd2eab4c4b

View File

@@ -80,10 +80,10 @@ export default function Gallery({
images={images} images={images}
currentIndex={currentIndex} currentIndex={currentIndex}
direction={direction} direction={direction}
isHovered={isHovered}
setIsHovered={setIsHovered}
goToPrevious={goToPrevious} goToPrevious={goToPrevious}
goToNext={goToNext} goToNext={goToNext}
setDirection={setDirection}
setCurrentIndex={setCurrentIndex}
/> />
<div <div
className="relative w-full overflow-hidden rounded-xl bg-base-100-tr82 shadow-sm" className="relative w-full overflow-hidden rounded-xl bg-base-100-tr82 shadow-sm"
@@ -190,22 +190,25 @@ function GalleryFullscreen({
images, images,
currentIndex, currentIndex,
direction, direction,
isHovered,
setIsHovered,
goToPrevious, goToPrevious,
goToNext, goToNext,
setDirection,
setCurrentIndex,
}: { }: {
dialogRef: React.RefObject<HTMLDialogElement | null>; dialogRef: React.RefObject<HTMLDialogElement | null>;
images: number[]; images: number[];
currentIndex: number; currentIndex: number;
direction: number; direction: number;
isHovered: boolean;
setIsHovered: (hovered: boolean) => void;
goToPrevious: () => void; goToPrevious: () => void;
goToNext: () => void; goToNext: () => void;
setDirection: (direction: number) => void;
setCurrentIndex: (index: number) => void;
}) { }) {
const [width, setWidth] = useState(0); const [width, setWidth] = useState(0);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const thumbnailContainerRef = useRef<HTMLDivElement>(null);
const hideTimeoutRef = useRef<number | null>(null);
const [isHovered, setIsHovered] = useState(true);
useEffect(() => { useEffect(() => {
const updateWidth = () => { const updateWidth = () => {
@@ -221,6 +224,29 @@ function GalleryFullscreen({
}; };
}, []); }, []);
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);
}
return () => {
window.removeEventListener("mousemove", handleMouseMove);
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
}
};
}, [dialogRef.current?.open, setIsHovered]);
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (dialogRef.current?.open) { if (dialogRef.current?.open) {
@@ -242,6 +268,15 @@ function GalleryFullscreen({
}; };
}, [dialogRef, goToPrevious, goToNext]); }, [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 ( return (
<dialog <dialog
ref={dialogRef} ref={dialogRef}
@@ -249,7 +284,6 @@ function GalleryFullscreen({
dialogRef.current?.close(); dialogRef.current?.close();
}} }}
className="modal" className="modal"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
> >
<div <div
@@ -310,14 +344,39 @@ function GalleryFullscreen({
<MdOutlineChevronRight size={24} /> <MdOutlineChevronRight size={24} />
</button> </button>
{/* 全屏模式下的指示器 */} {/* 图片缩略图列表 */}
<div <div
className={`absolute bottom-4 left-1/2 -translate-x-1/2 transition-opacity ${ className={`absolute bottom-4 left-1/2 -translate-x-1/2 transition-opacity ${
isHovered ? "opacity-100" : "opacity-0" isHovered ? "opacity-100" : "opacity-0"
}`} }`}
onClick={(e) => e.stopPropagation()}
> >
<div className="bg-base-100/20 px-2 py-1 rounded-full text-xs"> <div
{currentIndex + 1} / {images.length} ref={thumbnailContainerRef}
className="flex gap-2 overflow-x-auto max-w-[80vw] px-2 py-2 bg-base-100/60 rounded-xl scrollbar-thin scrollbar-thumb-base-content/30 scrollbar-track-transparent"
>
{images.map((imageId, index) => (
<button
key={index}
className={`flex-shrink-0 w-16 h-16 rounded-lg overflow-hidden transition-all ${
index === currentIndex
? "ring-2 ring-primary scale-110"
: "opacity-60 hover:opacity-100"
}`}
onClick={(e) => {
e.stopPropagation();
const newDirection = index > currentIndex ? 1 : -1;
setDirection(newDirection);
setCurrentIndex(index);
}}
>
<img
src={network.getResampledImageUrl(imageId)}
alt={`Thumbnail ${index + 1}`}
className="w-full h-full object-cover"
/>
</button>
))}
</div> </div>
</div> </div>