コンテンツにスキップ

ntorelabo

Front End Developer

  • pickup
  • PHP
    • WordPress
    • Laravel
    • PHP技術者認定試験
    • ステータスコード
  • Docker
  • AWS
  • Typing Game
  • site map
  • JavaScript
    • React
    • Next.js
    • TypeScript/React開発者向け基本技術チェックリスト
  • search

ntorelabo

Front End Developer

  • search
  • pickup
  • PHP
    • WordPress
    • Laravel
    • PHP技術者認定試験
    • ステータスコード
  • Docker
  • AWS
  • Typing Game
  • site map
  • JavaScript
    • React
    • Next.js
    • TypeScript/React開発者向け基本技術チェックリスト

Next.js + TypeScriptで作る!画像ギャラリー実装(map関数)

  • ホームページ
  • Next.js + TypeScriptで作る!画像ギャラリー実装(map関数)
  • 11月, 月, 2022
  • Next.js , TypeScript
Next.js + TypeScriptで作る!画像ギャラリー実装(map関数)

Contents [hide]

  • 1 完成イメージ
  • 2 ソースファイル
    • 2.1 map関数について

完成イメージ

このチュートリアルでは、以下のような機能を持つギャラリーを作成します

ソースファイル

imageGallery.tsx

// src/components/imageGallery.tsx
'use client'
import { useState } from 'react'
import { X, ChevronLeft, ChevronRight } from 'lucide-react'  // Lucide Reactのアイコンを使用
import styles from '@/styles/imageGallery.module.css'

const ImageGallery = () => {
  const [selectedImage, setSelectedImage] = useState<string | null>(null);
  const [currentIndex, setCurrentIndex] = useState<number>(0);

  const images = [
    "https://bizlabo.site/sample-img/column1.jpg",
    "https://bizlabo.site/sample-img/column2.jpg",
    "https://bizlabo.site/sample-img/column3.jpg",
    "https://bizlabo.site/sample-img/column4.jpg",
    "https://bizlabo.site/sample-img/column5.jpg",
    "https://bizlabo.site/sample-img/column6.jpg",
    "https://bizlabo.site/sample-img/column7.jpg",
    "https://bizlabo.site/sample-img/column8.jpg",
  ];

  // 引数はReact.MouseEvent型として、イベントオブジェクトを受け取り、イベント伝播を停止する
  const handleNext = (e: React.MouseEvent) => {
    e.stopPropagation(); // 親要素へのイベント伝播を停止
    setCurrentIndex((prev) => (prev + 1) % images.length); // 画像の数で割った余りを新しいインデックスとするため、画像の数を超えた場合は0に戻る
    setSelectedImage(images[(currentIndex + 1) % images.length]);
  };

  const handlePrev = (e: React.MouseEvent) => {
    e.stopPropagation();
    setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
    setSelectedImage(images[(currentIndex - 1 + images.length) % images.length]);
  };

  return (
    <div className={styles.container}>
      <div className={styles.grid}>
        {images.map((image, index) => (
          <img
            key={index}
            src={image}
            alt={`画像 ${index + 1}`}
            className={styles.image}
            onClick={() => {
              setSelectedImage(image);
              setCurrentIndex(index);
            }}
          />
        ))}
      </div>

      {selectedImage && (
        <div className={styles.modal} onClick={() => setSelectedImage(null)}>
          <div className={styles.modalContent}>
            <button className={styles.closeButton} onClick={() => setSelectedImage(null)}>
              <X size={32} />  {/* Xアイコン */}
            </button>
            <button className={styles.prevButton} onClick={handlePrev}>
              <ChevronLeft size={40} />  {/* 左矢印アイコン */}
            </button>
            <img
              src={selectedImage}
              alt="拡大画像"
              className={styles.modalImage}
              onClick={(e) => e.stopPropagation()}
            />
            <button className={styles.nextButton} onClick={handleNext}>
              <ChevronRight size={40} />  {/* 右矢印アイコン */}
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

export default ImageGallery;

imageGallery.module.css

.modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modalContent {
  position: relative;
  max-width: 90vw;
  max-height: 90vh;
}

.closeButton {
  position: absolute;
  top: -40px; /* 画像の上に配置 */
  right: -40px; /* 画像の右に配置 */
  background: none;
  border: none;
  color: white;
  cursor: pointer;
  padding: 8px;
  z-index: 1;
  transition: all 0.2s;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.closeButton:hover {
  background-color: rgba(255, 255, 255, 0.1);
  transform: scale(1.1);
}

.prevButton,
.nextButton {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: white;
  cursor: pointer;
  padding: 16px;
  z-index: 1;
  transition: all 0.2s;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.prevButton:hover,
.nextButton:hover {
  background-color: rgba(255, 255, 255, 0.1);
  transform: translateY(-50%) scale(1.1);
}

.prevButton {
  left: -60px;
}

.nextButton {
  right: -60px;
}

/* 既存のスタイル */
.container {
  padding: 1rem;
}

.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}

.image {
  width: 100%;
  height: auto;
  object-fit: contain;
  cursor: pointer;
  transition: transform 0.2s;
}

.image:hover {
  transform: scale(1.05);
}

.modalImage {
  max-height: 90vh;
  max-width: 90vw;
  object-fit: contain;
}

@media (min-width: 768px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 1024px) {
  .grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

map関数について

map関数の特徴

  1. 元の配列の各要素に対して処理を行う
  2. 新しい配列を返す
map関数の重要な2つの特徴 元の配列: [1, 2, 3] 特徴1: 各要素に対して同じ処理を適用 1 → multiply by 2 → 2 2 → multiply by 2 → 4 3 → multiply by 2 → 6 特徴2: 新しい配列を返す(元の配列は変更されない) 元の配列: [1, 2, 3] (変更されない) 新しい配列: [2, 4, 6] (処理結果の新しい配列)
最近の投稿
  • React Hook Form(Zodで型推論)Notionクローンアプリの認証、フォーム送信で実装 2025年10月12日
  • 【JavaScript】非同期処理をコールバック関数をしようせずにPromiseで実装できる理由 2025年10月9日
  • Web制作受注後の進め方(ヒアリング→デザイン→コーディング) 2025年10月8日
  • 【WordPress】WPGraphQLプラグイン 2025年10月8日
  • 個人アカウントのリポジトリをGithub Organizationに移管(Amplify ) 2025年10月3日
  • WordPressサイト不正アクセスの検知 2025年9月25日
  • pnpm 2025年9月25日
  • WordPressサイトのサブディレクトリ→サブドメイン移行手順 2025年7月6日
  • Laravelでリッチテキストエディタで使いやすい画像アップロード機能を実装(TinyMCE) 2025年6月22日
  • 【PHP】出力バッファリング制御 2025年5月22日
  • basic認証のパスワードを忘れた場合 2025年5月14日
  • PHP 8.0〜から実装で便利な機能(名前付き引数、match) 2025年5月11日
  • さくらインターネットからWHOIS登録メールアドレスの利用廃止のメールが来た 2025年5月9日
  • スムーズにスクロールする処理をjQueryで実装 2025年5月5日
  • Gemini for Google Workspace 活用術 2025年4月24日
  • Google Cloud の始め方 2025年4月20日
  • 【PHP】switch分の書き方、PHP 7.3 から PHP7.4の変更点 2025年4月19日
  • 【PHP】新しい方の指定方法「nullable」 2025年3月25日
  • 「PHPでJSONを扱う」json_encodeとjson_decode完全ガイド 2025年3月23日
  • 【WordPress自作テーマ開発】固定ページを自動作成する方法(下層ページ実装) 2025年3月18日
About Us

Ready To Start Work With Us?

Felis consequat magnis est fames sagittis ultrices placerat sodales porttitor quisque.

Get a Quote
recent posts
  • React Hook Form(Zodで型推論)Notionクローンアプリの認証、フォーム送信で実装
  • 【JavaScript】非同期処理をコールバック関数をしようせずにPromiseで実装できる理由
  • Web制作受注後の進め方(ヒアリング→デザイン→コーディング)
  • 【WordPress】WPGraphQLプラグイン
  • 個人アカウントのリポジトリをGithub Organizationに移管(Amplify )
  • WordPressサイト不正アクセスの検知
  • pnpm
  • WordPressサイトのサブディレクトリ→サブドメイン移行手順
Opening Hours
Week Days 10:00 - 17:00
Saturday 10:00 - 15:00
Sunday Day Off
Contact us
popular posts
  • WordPressで画像を拡大表示するなら「Firelight Lightbox」(旧名:Easy Fancybox) 1.2k件のビュー
  • 【WordPress】カスタムフィールド作成の仕方「ACF」Advanced Custom Fieldsの使用法 762件のビュー
  • 【チートシート】Linuc Lv1-102 実務での使用例も解説 710件のビュー
  • My-Snow-Monkey使用例、子テーマによるカスタマイズ 515件のビュー
  • 【WordPressエラー】「Warning: Undefined variable $post in …」「Warning: Attempt to read property “ID” on null in …」 484件のビュー
  • Twitter、Facebookのタイムラインを埋め込む 415件のビュー
  • ローカル、グローバルIPアドレスの違いと確認手順 408件のビュー
  • 【Next.js】ライブラリ使わないでカルーセルスライダー 325件のビュー
  • AWS認定ソリューションアーキテクト-アソシエイトレベル 248件のビュー
  • WordPress自作、カスタマイズ 229件のビュー
Search
Copyright © 2025 ntorelabo | Powered by Desert Themes