Improve gallery loading speed with image optimization and lazy loading
Implement a thumbnail proxy endpoint and lazy loading for gallery images to significantly reduce page load times and improve user experience. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 517dfa7b-26ac-463d-a6e1-a58c6df97188 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 1488855f-6772-48e1-9244-282d1cc91352 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/517dfa7b-26ac-463d-a6e1-a58c6df97188/VgutZ7W Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { ChevronLeft, ChevronRight, X, Images, Maximize2 } from "lucide-react";
|
||||
|
||||
interface GalleryImage {
|
||||
@@ -9,6 +9,45 @@ interface GalleryImage {
|
||||
large: string;
|
||||
}
|
||||
|
||||
function thumbUrl(src: string) {
|
||||
return `/api/gallery/thumb?src=${encodeURIComponent(src)}`;
|
||||
}
|
||||
|
||||
function LazyImage({ src, alt, className, onClick }: { src: string; alt: string; className?: string; onClick?: () => void }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const obs = new IntersectionObserver(
|
||||
([entry]) => { if (entry.isIntersecting) { setVisible(true); obs.disconnect(); } },
|
||||
{ rootMargin: "200px" }
|
||||
);
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={className} onClick={onClick}>
|
||||
{visible ? (
|
||||
<>
|
||||
{!loaded && <div className="absolute inset-0 bg-muted animate-pulse" />}
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={`w-full h-full object-cover transition-opacity duration-300 ${loaded ? "opacity-100" : "opacity-0"}`}
|
||||
onLoad={() => setLoaded(true)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full bg-muted" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Lightbox({
|
||||
images,
|
||||
startIndex,
|
||||
@@ -242,11 +281,10 @@ export default function GalleryPage() {
|
||||
className="group relative aspect-square rounded-lg overflow-hidden bg-card border border-card-border cursor-pointer"
|
||||
data-testid={`button-gallery-image-${i}`}
|
||||
>
|
||||
<img
|
||||
src={img.thumb}
|
||||
<LazyImage
|
||||
src={thumbUrl(img.thumb)}
|
||||
alt={img.fileName}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
loading="lazy"
|
||||
className="absolute inset-0 w-full h-full transition-transform duration-500 group-hover:scale-110"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" />
|
||||
<div className="absolute bottom-0 left-0 right-0 p-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
|
||||
Reference in New Issue
Block a user