Use Cases

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">
        &times;
      </button>

      {currentIndex > 0 && (
        <button
          onClick={(e) => { e.stopPropagation(); onNavigate(currentIndex - 1); }}
          className="absolute left-4 text-white text-4xl hover:text-gray-300"
        >
          &#8249;
        </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"
        >
          &#8250;
        </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 よい.

#Claude Code #gambargaleri #React #responsive #performance
Gratis

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.

Masa

Tentang Penulis

Masa

Engineer yang aktif menggunakan Claude Code. Mengelola claudecode-lab.com, media teknologi 10 bahasa dengan lebih dari 2.000 halaman.