import { useState, useEffect, useCallback, memo } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { X, ChevronLeft, ChevronRight } from 'lucide-react'; // 인디케이터 컴포넌트 - CSS transition 사용으로 GPU 가속 const LightboxIndicator = memo(function LightboxIndicator({ count, currentIndex, goToIndex }) { const translateX = -(currentIndex * 18) + 100 - 6; return (
{/* 양옆 페이드 그라데이션 */}
{/* 슬라이딩 컨테이너 */}
{Array.from({ length: count }).map((_, i) => (
); }); // 라이트박스 공통 컴포넌트 function Lightbox({ images, currentIndex, isOpen, onClose, onIndexChange }) { const [imageLoaded, setImageLoaded] = useState(false); const [slideDirection, setSlideDirection] = useState(0); // 이전/다음 네비게이션 const goToPrev = useCallback(() => { if (images.length <= 1) return; setImageLoaded(false); setSlideDirection(-1); onIndexChange((currentIndex - 1 + images.length) % images.length); }, [images.length, currentIndex, onIndexChange]); const goToNext = useCallback(() => { if (images.length <= 1) return; setImageLoaded(false); setSlideDirection(1); onIndexChange((currentIndex + 1) % images.length); }, [images.length, currentIndex, onIndexChange]); const goToIndex = useCallback((index) => { if (index === currentIndex) return; setImageLoaded(false); setSlideDirection(index > currentIndex ? 1 : -1); onIndexChange(index); }, [currentIndex, onIndexChange]); // 라이트박스 열릴 때 body 스크롤 숨기기 useEffect(() => { if (isOpen) { document.documentElement.style.overflow = 'hidden'; document.body.style.overflow = 'hidden'; } else { document.documentElement.style.overflow = ''; document.body.style.overflow = ''; } return () => { document.documentElement.style.overflow = ''; document.body.style.overflow = ''; }; }, [isOpen]); // 키보드 이벤트 핸들러 useEffect(() => { if (!isOpen) return; const handleKeyDown = (e) => { switch (e.key) { case 'ArrowLeft': goToPrev(); break; case 'ArrowRight': goToNext(); break; case 'Escape': onClose(); break; default: break; } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [isOpen, goToPrev, goToNext, onClose]); // 이미지가 바뀔 때 로딩 상태 리셋 useEffect(() => { setImageLoaded(false); }, [currentIndex]); return ( {isOpen && images.length > 0 && ( {/* 내부 컨테이너 */}
{/* 닫기 버튼 */} {/* 이전 버튼 */} {images.length > 1 && ( )} {/* 로딩 스피너 */} {!imageLoaded && (
)} {/* 이미지 */}
e.stopPropagation()} onLoad={() => setImageLoaded(true)} initial={{ x: slideDirection * 100 }} animate={{ x: 0 }} transition={{ duration: 0.25, ease: 'easeOut' }} />
{/* 다음 버튼 */} {images.length > 1 && ( )} {/* 인디케이터 */} {images.length > 1 && ( )}
)}
); } export default Lightbox;