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 よい.
PDF Gratis: Cheatsheet Claude Code dalam 5 Menit
Cukup masukkan emailmu dan kami akan langsung mengirim cheatsheet PDF A4 satu halaman.
Kami menjaga data pribadimu dengan aman dan tidak pernah mengirim spam.
Tentang Penulis
Masa
Engineer yang aktif menggunakan Claude Code. Mengelola claudecode-lab.com, media teknologi 10 bahasa dengan lebih dari 2.000 halaman.
Artikel Terkait
7 pemeriksaan sebelum menerbitkan artikel Claude Code multibahasa setiap hari
Checklist praktis agar artikel Claude Code multibahasa yang diterbitkan setiap hari tidak kehilangan locale, tidak merusak CTA, dan tidak meninggalkan halaman lama di production.
Apa itu Codex Automations? Membiarkan AI mengurus konten saat kamu tidur
Panduan praktis memakai Codex Automations untuk analytics, artikel, CTA, deploy, dan monetisasi.
Claude Code × GCP Cloud Functions Panduan Lengkap | Pengembangan Serverless Super Cepat
Optimalkan GCP Cloud Functions dengan Claude Code. Implementasikan trigger HTTP/Pub/Sub/Firestore, pengujian lokal, dan otomatisasi deployment dengan contoh kode nyata dari pengalaman Masa.