Swiperで動的なスライド形式に表示させたい!

Swiperで動的なスライド形式に表示させたい!

投稿日: 2025年03月03日

学習振り返り
要約
  • Swiperは、Webやモバイルアプリでスライダーを実装するための軽量なJavaScriptライブラリです。
  • 簡単にインストールでき、カスタマイズ性が高く、自動再生やナビゲーション機能も搭載しています。
  • 実際にカフェ情報をスライド形式で表示する例も紹介し、UIの向上に貢献します。

はじめに

オリアプで、スライド形式に表示させたくて今回はSwiperを使ってみたので、
皆さんにも共有したいと思います!

Swiper

Swiperは、Webアプリやモバイルアプリでスライダー(カルーセル)を実装するための軽量かつ高機能なJavaScriptライブラリです。
他のライブラリに依存せず、JavaScriptの他にjQuery、React、Vue.jsでも使えます。
詳しくは、以下の公式ページをご覧ください。

以下、完成イメージです。(動画貼れないのでURL先で見てください)
AmazonとかzozotownのECサイトとかで、皆さんもよく見かけるスライド形式なやつです!
スライドして商品を眺められるアレです笑
手動もできますが、動的にもできるのでおしゃれです笑


まずは、インストール

$ npm install swiper


Swiperのスタイルシートのインポート
Swiperではデフォルトスタイルを適用するために、以下をインポートします。
これは、スライダーの基本的なデザインやアニメーションを適切に表示させるために必要です。

import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";

swiper/css: Swiperの基本的なスタイルのみを含むスタイルシートとなります。
基本的なレイアウトを提供するものです。

swiper/css/navigation: Swiper全てのモジュール(ナビゲーション、ページネーション)のスタイルを含んでいます。
つまり、追加でスタイルシートをインポートしなくても、各機能が使える状態となります。

swiper/css/pagination: ページネーション機能に必要なスタイルのみを含むスタイルシートです。

ページネーション: スライドの下部などに配置されるドットや番号のことを指します。
ナビゲーション: スライダーの左右に配置される矢印のことです。
スタイルシート類


Swiperコンポーネントのインポート
Swiperを使用するために、必要なコンポーネントはライブラリをインポートします。

import { Swiper, SwiperSlide } from "swiper/react";
import { Autoplay, Navigation, Pagination } from "swiper/modules";

Swiperコンポーネント: スライダー全体のコンテナとなるコンポーネントです。
スライダーの設定やオプションをこのコンポーネントに渡す感じです。

SwiperSlideコンポーネント:各スライドの内容を定義するためのコンポーネントです。
コンポーネントの小要素として配置し、スライドごとに異なるコンテンツを設定できます。

Autoplayモジュール:​ スライダーを自動的に再生するモジュールです。
例えば、数秒ごとに次のスライドへ自動的に切り替わるように設定できます。

Navigationモジュール: 先程紹介したナビゲーションのことで、スライダーの左右に配置し、「前へ」「次へ」とナビゲーションボタン(矢印)の機能を提供してくれます。
手動で、スライドが切り替わることができます。

Paginationモジュール:これも先程紹介したページネーションのことで、スライダーの下部に配置されるドットのことです。現在のスライドの全体の位置がどの辺りかを視覚的に示し、ユーザーが特定のスライドに直接移動することが可能です。


具体的な使用例

import React from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';

const MySlider = () => {
  return (
    <Swiper
      modules={[Autoplay, Navigation, Pagination]}
      spaceBetween={30}
      slidesPerView={1}
      loop={true}
      autoplay={{ delay: 3000, disableOnInteraction: false }}
      navigation
      pagination={{ clickable: true }}
    >
      <SwiperSlide>
        <img src="image1.jpg" alt="Slide 1" />
      </SwiperSlide>
      <SwiperSlide>
        <img src="image2.jpg" alt="Slide 2" />
      </SwiperSlide>
      <SwiperSlide>
        <img src="image3.jpg" alt="Slide 3" />
      </SwiperSlide>
    </Swiper>
  );
};

export default MySlider;

ポイント

  • modulesプロパティで使用するモジュール(AutoplayNavigationPagination)を指定する

  • spaceBetweenはスライド間のスペースを指定する。

  • slidesPerViewは同時に表示させるスライドの枚数を指定。

  • looptrueにすると、スライドが最後に到達した後、最初のスライド戻り、ループし続ける。

  • autoplayオプションで、自動再生を設定する。delayは次のスライド画面に切り替わる時間を設定。disableOnInteractionfalseにすると、ユーザーがスライドを操作しても自動再生が停止しない設定になる。

  • navigationは先ほどの説明と同様です。

  • paginationも説明は同様です。

実際にSwiperを使用した実装したコード

以下は、投稿フォームで登録したカフェの情報を、最新情報とおすすめカフェ情報と項目で分けてスライド形式で表示させるコードです。
投稿が1つの情報しかない場合は、通常のカード形式にし、投稿が2個以上の場合はSwiperのオプション達(loop, nabigation, pagination)を使用してスライド形式にしてます。

"use client";
import React from "react";
import Image from "next/image";
import { Swiper, SwiperSlide } from "swiper/react";
import { Autoplay, Navigation, Pagination } from "swiper/modules";
import { Cafe } from "@/app/_types/Cafe";
import { timeAgo } from "../_utils/timeAgo";
import { RenderStars } from "../_utils/renderStars";
import "../../globals.css";

// Swiper のスタイルをインポート
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";

type Props = {
  cafes: Cafe[];
  onClick: (cafes: Cafe) => void;
};

// 最新カフェ投稿の表示
export const LatestCafeList: React.FC<Props> = ({ cafes, onClick }) => {
  if(cafes.length === 0) {
    return <p className="mb-[100px] text-[min(13vw,25px)] font-bold text-custom-red">最新情報の投稿がありません</p>
  }
  return (
    <div className="mt-[100px]">
      <h1 className="font-bold text-[min(13vw,30px)] text-center">最新情報</h1>
        <Swiper
        className={`mx-auto py-10 mt-[100px] bg-beige-200 rounded-lg shadow-md overflow-hidden ${
          cafes.length === 1 ? "max-w-[400px] h-[400px]" : "max-w-[800px] h-[400px]"
        }`}
        modules={[Autoplay, Navigation, Pagination]}
        spaceBetween={10}
        slidesPerView={Math.min(2, cafes.length)}
        loop={cafes.length > 1} // スライドが2つ以上のときのみループ
        autoplay={{
          delay: 3000,
          disableOnInteraction: false,
        }}
        navigation={cafes.length > 1} // 2つ以上のスライドがある場合のみナビゲーションを有効化
        pagination={cafes.length > 1 ? { clickable: true } : false} // 2つ以上のスライドがある場合のみページネーションを有効化
        watchOverflow={true} // スライドが1枚のときSwiperのレイアウト崩れを防ぐ
        breakpoints={{
          480: {
            slidesPerView: 1,
            spaceBetween: 10,
          },
          768: {
            slidesPerView: 2,
            spaceBetween: 20,
          },
          1024: {
            slidesPerView: Math.min(3, cafes.length),
            spaceBetween: 30,
          },
        }}
      >
        {cafes.map((cafe) => (
          <SwiperSlide key={cafe.id}>
            <div
              className="bg-beige-200 rounded-lg shadow-md overflow-hidden"
              onClick={() => onClick(cafe)}
            >
              <div className="relative w-full h-[200px]">
                <Image
                  src={cafe.thumbnailImage}
                  alt={cafe.cafeName}
                  className="rounded-lg object-cover"
                  fill
                />
              </div>
              <div className="p-5 h-[200px]">
                <h3 className="text-sm font-bold pt-2">{cafe.cafeName}</h3>
                <p className="text-sm font-bold pt-10">
                  {timeAgo(cafe.createdAt || cafe.updatedAt)}
                </p>
              </div>
            </div>
          </SwiperSlide>
        ))}
      </Swiper>
    </div>
  );
};

//おすすめカフェの表示
export const RecommendationCafeList: React.FC<Props> = ({ cafes, onClick }) => {
  if(cafes.length === 0) {
    return <p className="mt-[100px] text-[min(13vw,25px)] font-bold text-custom-red">おすすめカフェ情報の投稿がありません</p>
  }
  return (
    <div className="mt-[200px] mb-[130px]">
      <h1 className="font-bold text-[min(13vw,30px)] text-center">
        おすすめのカフェ情報
      </h1>
        <Swiper
            className={`mx-auto py-10 mt-[100px] bg-beige-200 rounded-lg shadow-md overflow-hidden ${
              cafes.length === 1 ? "max-w-[400px] h-[400px]" : "max-w-[800px] h-[400px]"
            }`}
          modules={[Autoplay, Navigation, Pagination]}
          spaceBetween={10}
          slidesPerView={Math.min(2, cafes.length)}
          loop={cafes.length > 1} // スライドが2つ以上のときのみループ
          autoplay={{
            delay: 3000,
            disableOnInteraction: false,
          }}
          navigation={cafes.length > 1} // 2つ以上のスライドがある場合のみナビゲーションを有効化
          pagination={cafes.length > 1 ? { clickable: true } : false} // 2つ以上のスライドがある場合のみページネーションを有効化
          watchOverflow={true} // スライドが1枚のときSwiperのレイアウト崩れを防ぐ
          breakpoints={{
            480: {
              slidesPerView: 1,
              spaceBetween: 10,
            },
            768: {
              slidesPerView: 2,
              spaceBetween: 10,
            },
            1024: {
              slidesPerView: Math.min(3, cafes.length),
              spaceBetween: 20,
            },
          }}
        >
          {cafes.map((cafe) => (
            <SwiperSlide key={cafe.id}>
              <div
                className="bg-beige-200 rounded-lg shadow-md overflow-hidden"
                onClick={() => onClick(cafe)}//クリックされたカフェを渡す 
              >
                <div className="relative w-full h-[200px]">
                  <Image
                    src={cafe.thumbnailImage}
                    alt={cafe.cafeName}
                    fill
                    className="rounded-lg object-cover"
                  />
                </div>
                <div className="p-5 h-[200px]">
                  <h3 className="text-sm font-bold pt-5">{cafe.cafeName}</h3>
                  <p className="text-sm font-bold pt-5">
                    星評価: {RenderStars(cafe.starRating)}
                  </p>
                  <p className="text-sm font-bold pt-5">エリア  {cafe.area}</p>
                </div>
              </div>
            </SwiperSlide>
          ))}
        </Swiper>
    </div>
  );
};


さいご

Swiperを使った、UIでおしゃれなスライドを表示させてみました。
興味がありましたら使ってみてはいかがでしょうか。

シェア!

Threads
記事一覧に戻る
Threads
0