Membangun an Image Gallery dengan Claude Code
Learn about building an image gallery using Claude Code. Includes practical code examples.
gambargaleri dengan Claude Code: pembangunan
gambargaleri ポートフォリオ、ECサイト、写真berbagiaplikasi dll.、多く 場面 diperlukan なるkomponen.Claude Code 使えば、マソンリーlayout、ライトボックス、遅延loading 備えた高品質なgaleri efisien pembangunan bisa dilakukan.
要件を指示 galeri
> gambargalerikomponen buatkan.
> マソンリーlayout、ライトボックスtampilan、
> 遅延loading、kategorifiltering implementasikan.
> レスポンシブdukungan dengan 。
マソンリーlayout
// src/components/MasonryGallery.tsx
'use client';
import { useState, useMemo } from 'react';
interface GalleryImage {
id: string;
src: string;
alt: string;
width: number;
height: number;
category: string;
}
interface Props {
images: GalleryImage[];
columns?: number;
}
export function MasonryGallery({ images, columns = 3 }: Props) {
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);
const categories = useMemo(
() => [...new Set(images.map((img) => img.category))],
[images]
);
const filteredImages = useMemo(
() => selectedCategory
? images.filter((img) => img.category === selectedCategory)
: images,
[images, selectedCategory]
);
// カラム gambar 分配
const columnImages = useMemo(() => {
const cols: GalleryImage[][] = Array.from({ length: columns }, () => []);
filteredImages.forEach((img, i) => {
cols[i % columns].push(img);
});
return cols;
}, [filteredImages, columns]);
return (
<div>
{/* カテゴリフィルター */}
<div className="flex gap-2 mb-6 flex-wrap">
<button
onClick={() => setSelectedCategory(null)}
className={`px-4 py-2 rounded-full text-sm ${
!selectedCategory ? 'bg-blue-600 text-white' : 'bg-gray-100 dark:bg-gray-800'
}`}
>
すべて
</button>
{categories.map((cat) => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`px-4 py-2 rounded-full text-sm ${
selectedCategory === cat ? 'bg-blue-600 text-white' : 'bg-gray-100 dark:bg-gray-800 dark:text-gray-300'
}`}
>
{cat}
</button>
))}
</div>
{/* マソンリーグリッド */}
<div className="flex gap-4">
{columnImages.map((col, colIndex) => (
<div key={colIndex} className="flex-1 flex flex-col gap-4">
{col.map((img) => (
<div
key={img.id}
onClick={() => setLightboxIndex(filteredImages.indexOf(img))}
className="cursor-pointer overflow-hidden rounded-lg hover:opacity-90 transition"
>
<img
src={img.src}
alt={img.alt}
loading="lazy"
className="w-full h-auto"
style={{ aspectRatio: `${img.width}/${img.height}` }}
/>
</div>
))}
</div>
))}
</div>
{/* ライトボックス */}
{lightboxIndex !== null && (
<Lightbox
images={filteredImages}
currentIndex={lightboxIndex}
onClose={() => setLightboxIndex(null)}
onNavigate={setLightboxIndex}
/>
)}
</div>
);
}
ライトボックスkomponen
// src/components/Lightbox.tsx
'use client';
import { useEffect, useCallback } from 'react';
interface Props {
images: GalleryImage[];
currentIndex: number;
onClose: () => void;
onNavigate: (index: number) => void;
}
export function Lightbox({ images, currentIndex, onClose, onNavigate }: Props) {
const current = images[currentIndex];
const handleKeyDown = useCallback((e: KeyboardEvent) => {
switch (e.key) {
case 'Escape':
onClose();
break;
case 'ArrowLeft':
if (currentIndex > 0) onNavigate(currentIndex - 1);
break;
case 'ArrowRight':
if (currentIndex < images.length - 1) onNavigate(currentIndex + 1);
break;
}
}, [currentIndex, images.length, onClose, onNavigate]);
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = '';
};
}, [handleKeyDown]);
return (
<div
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center"
onClick={onClose}
>
<button onClick={onClose} className="absolute top-4 right-4 text-white text-3xl z-10">
×
</button>
{currentIndex > 0 && (
<button
onClick={(e) => { e.stopPropagation(); onNavigate(currentIndex - 1); }}
className="absolute left-4 text-white text-4xl hover:text-gray-300"
>
‹
</button>
)}
<img
src={current.src}
alt={current.alt}
className="max-h-[90vh] max-w-[90vw] object-contain"
onClick={(e) => e.stopPropagation()}
/>
{currentIndex < images.length - 1 && (
<button
onClick={(e) => { e.stopPropagation(); onNavigate(currentIndex + 1); }}
className="absolute right-4 text-white text-4xl hover:text-gray-300"
>
›
</button>
)}
<div className="absolute bottom-4 text-white text-sm">
{currentIndex + 1} / {images.length}
</div>
</div>
);
}
Optimasi Performa
gambar performa 高める untuk 、berikut ポイント Claude Code penambahan 依頼し.
- srcsetatribut: デバイス幅 応じたgambarサイズ penyediaan
- BlurHash: loading中 placeholdertampilan
- WebP/AVIF: 次世代format 自動konversi
- CDNdistribusi: CloudflareやCloudFront cache
Artikel Terkait
gambarpemrosesan全般 gambarpemrosesan implementasi、performaoptimasi performaoptimasipanduan juga bisa dijadikan referensi.
gambaroptimasi Cloudinary(cloudinary.com) seperti service pemanfaatan juga 検討 dan よい.
Related Posts
Cara Mempercepat Side Project dengan Claude Code [Dengan Contoh]
Pelajari cara mempercepat project development personal secara drastis menggunakan Claude Code. Dilengkapi contoh nyata dan workflow praktis dari ide hingga deployment.
Cara Mengotomatisasi Refactoring dengan Claude Code
Pelajari cara mengotomatisasi code refactoring secara efisien menggunakan Claude Code. Dilengkapi prompt praktis dan pola refactoring konkret untuk project nyata.
Panduan Lengkap Konfigurasi CORS dengan Claude Code
Pelajari tentang panduan lengkap konfigurasi CORS menggunakan Claude Code. Dilengkapi tips praktis dan contoh kode.