# 摄影作品集网站 - 前端开发文档 ## 1. 技术架构 ### 1.1 技术栈选择 - **框架**:Next.js 14 (App Router) - **样式**:Tailwind CSS + CSS Modules - **状态管理**:Zustand + React Query - **图片处理**:Sharp + Next.js Image - **动画**:Framer Motion - **UI组件**:Headless UI + 自定义组件 ### 1.2 项目结构 ``` src/ ├── app/ # App Router页面 │ ├── layout.tsx # 根布局 │ ├── page.tsx # 首页 │ ├── gallery/ # 作品集页面 │ ├── timeline/ # 时间线页面 │ └── about/ # 关于页面 ├── components/ # 可复用组件 │ ├── ui/ # 基础UI组件 │ ├── gallery/ # 作品集相关组件 │ ├── timeline/ # 时间线组件 │ └── layout/ # 布局组件 ├── hooks/ # 自定义Hooks ├── lib/ # 工具函数 ├── stores/ # 状态管理 ├── types/ # TypeScript类型 └── styles/ # 全局样式 ``` ## 2. 核心功能实现 ### 2.1 图片管理系统 #### 多格式图片处理 ```typescript // lib/imageUtils.ts export interface ImageFormats { raw?: string; // RAW文件路径 jpg: string; // JPG文件路径 webp?: string; // WebP优化版本 thumb: string; // 缩略图 } export interface PhotoData { id: string; title: string; description?: string; date: string; formats: ImageFormats; metadata: { camera?: string; lens?: string; settings?: { iso: number; aperture: string; shutter: string; focal: string; }; }; collections: string[]; } ``` #### 响应式图片组件 ```typescript // components/ui/ResponsiveImage.tsx interface ResponsiveImageProps { photo: PhotoData; sizes: string; priority?: boolean; className?: string; onClick?: () => void; } export function ResponsiveImage({ photo, sizes, priority, className, onClick }: ResponsiveImageProps) { return (
{photo.title}
); } ``` ### 2.2 作品集网格系统 #### 自适应网格布局 ```typescript // components/gallery/PhotoGrid.tsx export function PhotoGrid({ photos }: { photos: PhotoData[] }) { return (
{photos.map((photo, index) => (
openLightbox(photo)} />
))}
); } ``` #### 虚拟化长列表 ```typescript // hooks/useVirtualGrid.ts export function useVirtualGrid(items: PhotoData[], itemHeight: number) { const [visibleItems, setVisibleItems] = useState([]); const [startIndex, setStartIndex] = useState(0); const handleScroll = useCallback((scrollTop: number, containerHeight: number) => { const newStartIndex = Math.floor(scrollTop / itemHeight); const endIndex = Math.min( newStartIndex + Math.ceil(containerHeight / itemHeight) + 2, items.length ); setStartIndex(newStartIndex); setVisibleItems(items.slice(newStartIndex, endIndex)); }, [items, itemHeight]); return { visibleItems, startIndex, handleScroll }; } ``` ### 2.3 时间线功能 #### 时间线数据结构 ```typescript // types/timeline.ts export interface TimelineGroup { year: number; months: { [month: number]: { photos: PhotoData[]; count: number; }; }; } export interface TimelineData { groups: TimelineGroup[]; totalPhotos: number; yearRange: [number, number]; } ``` #### 时间线组件 ```typescript // components/timeline/Timeline.tsx export function Timeline({ data }: { data: TimelineData }) { const [selectedYear, setSelectedYear] = useState(data.yearRange[1]); return (
{/* 年份导航 */} g.year)} selectedYear={selectedYear} onYearSelect={setSelectedYear} /> {/* 时间线内容 */}
{data.groups .filter(group => group.year === selectedYear) .map(group => ( ))}
); } ``` ### 2.4 灯箱组件 #### 全功能图片查看器 ```typescript // components/ui/Lightbox.tsx export function Lightbox() { const { isOpen, currentPhoto, photos, currentIndex } = useLightboxStore(); const [isZoomed, setIsZoomed] = useState(false); const [position, setPosition] = useState({ x: 0, y: 0 }); if (!isOpen || !currentPhoto) return null; return ( {/* 图片区域 */}
{/* 导航控件 */} {/* 信息面板 */}
); } ``` ## 3. 性能优化策略 ### 3.1 图片优化 #### 渐进式加载 ```typescript // hooks/useProgressiveImage.ts export function useProgressiveImage(src: string, placeholderSrc: string) { const [currentSrc, setCurrentSrc] = useState(placeholderSrc); const [loading, setLoading] = useState(true); useEffect(() => { const img = new Image(); img.src = src; img.onload = () => { setCurrentSrc(src); setLoading(false); }; }, [src]); return { src: currentSrc, loading }; } ``` #### 智能预加载 ```typescript // hooks/useImagePreloader.ts export function useImagePreloader(photos: PhotoData[], currentIndex: number) { useEffect(() => { // 预加载当前图片的前后各2张 const preloadRange = 2; const start = Math.max(0, currentIndex - preloadRange); const end = Math.min(photos.length, currentIndex + preloadRange + 1); for (let i = start; i < end; i++) { if (i !== currentIndex) { const img = new Image(); img.src = photos[i].formats.jpg; } } }, [photos, currentIndex]); } ``` ### 3.2 状态管理优化 #### Zustand Store设计 ```typescript // stores/galleryStore.ts interface GalleryState { photos: PhotoData[]; collections: Collection[]; currentCollection: string | null; loading: boolean; error: string | null; // Actions setPhotos: (photos: PhotoData[]) => void; setCurrentCollection: (id: string | null) => void; addPhotos: (photos: PhotoData[]) => void; } export const useGalleryStore = create((set, get) => ({ photos: [], collections: [], currentCollection: null, loading: false, error: null, setPhotos: (photos) => set({ photos }), setCurrentCollection: (id) => set({ currentCollection: id }), addPhotos: (newPhotos) => set((state) => ({ photos: [...state.photos, ...newPhotos] })), })); ``` ### 3.3 代码分割 #### 动态导入 ```typescript // app/gallery/page.tsx const LazyPhotoGrid = dynamic(() => import('@/components/gallery/PhotoGrid'), { loading: () => , ssr: false }); const LazyLightbox = dynamic(() => import('@/components/ui/Lightbox'), { ssr: false }); ``` ## 4. 移动端优化 ### 4.1 触摸手势 ```typescript // hooks/useSwipeGesture.ts export function useSwipeGesture(onSwipeLeft: () => void, onSwipeRight: () => void) { const touchStart = useRef<{ x: number; y: number } | null>(null); const touchEnd = useRef<{ x: number; y: number } | null>(null); const handleTouchStart = (e: TouchEvent) => { touchEnd.current = null; touchStart.current = { x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY, }; }; const handleTouchEnd = () => { if (!touchStart.current || !touchEnd.current) return; const distance = touchStart.current.x - touchEnd.current.x; const isLeftSwipe = distance > 50; const isRightSwipe = distance < -50; if (isLeftSwipe) onSwipeLeft(); if (isRightSwipe) onSwipeRight(); }; return { handleTouchStart, handleTouchEnd }; } ``` ### 4.2 移动端导航 ```typescript // components/layout/MobileNavigation.tsx export function MobileNavigation() { const [isOpen, setIsOpen] = useState(false); return ( <> {/* 汉堡菜单按钮 */} {/* 侧边导航 */} {isOpen && ( )} ); } ``` ## 5. SEO优化 ### 5.1 元数据管理 ```typescript // app/gallery/[collection]/page.tsx export async function generateMetadata({ params }: { params: { collection: string } }) { const collection = await getCollection(params.collection); return { title: `${collection.name} | 摄影作品集`, description: collection.description, openGraph: { title: collection.name, description: collection.description, images: [collection.coverImage], }, }; } ``` ### 5.2 结构化数据 ```typescript // components/seo/StructuredData.tsx export function PhotoStructuredData({ photo }: { photo: PhotoData }) { const structuredData = { '@context': 'https://schema.org', '@type': 'Photograph', name: photo.title, description: photo.description, contentUrl: photo.formats.jpg, thumbnail: photo.formats.thumb, dateCreated: photo.date, creator: { '@type': 'Person', name: '摄影师姓名', }, }; return (