6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Vue3 + Swiper8] 独自デザインのナビゲーションとページネーションを実装する

Posted at

はじめに

高機能カルーセルライブラリといえば自分は Swiper をおすすめします。
Vue、React、Angularなどに対応し、カスタマイズ機能が豊富。

ただしバージョン毎にクセがあって、アップデートの度に導入トラブルで苦しめられていたのですが(自分の技量が足りない)、最新版の 8.0.5 ではスムーズに導入できたので備忘録として残します。

インストール

swiper をインストールするだけです。

$ yarn add swiper

もし css-loadervue-style-loader がなければインストールします。

$ yarn add --dev css-loader vue-style-loader

Webpack設定

この記事では Vue での開発環境が整っている前提ですので詳細は省きます。
webpack.config.jsmodule.rules に CSS設定を追加します。

webpack.config.js
module.exports = {
  // 〜略〜
  module: {
    rules: [
      // Vue設定
      {
        test: /\.vue$/,
        exclude: /node_modules/,
        use: [{
          loader: 'vue-loader',
        }],
      },
      // css設定
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader'],
      }
    ],
  },
  // 〜略〜
}

style-loader も入れたらエラーになった

上の結論になる前に style-loader も入れていたのですが、実行時にエラーになりました。
この辺の組み合わせはいまだによくわかってません。

// 実行時にエラー
use: ['vue-style-loader', 'style-loader', 'css-loader'],
// 問題なし
use: ['vue-style-loader', 'css-loader'],

コンポーネント実装

Vue3 の <script setup> 構文を使って書いています。

<script setup lang="ts">
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/css';

const images = [
  'https://i.picsum.photos/id/925/600/400.jpg?hmac=VxvBfviRrdHXsJ6_nbZ00wdqs0AEutxmI1ughFU8KQU',
  'https://i.picsum.photos/id/428/600/400.jpg?hmac=grP0p8M-3MohHSsDfu7ZXo9VGbL-OUSCDScCUZu8ps4',
  'https://i.picsum.photos/id/340/600/400.jpg?hmac=fUPfZKTRNEKns8xR8vPzDByTCfY9cEbh5lFLG8rP8H8',
  'https://i.picsum.photos/id/702/600/400.jpg?hmac=KxRAc7VlNyemdt4RTHqjBvy9heWN7FV0qHG_U2OyOjo',
];

// Swiperのインスタンスが返ってくる
const onSwiper = (swiper) => {
  console.log('swiper', swiper);
};

// スライド位置が変更された時に呼ばれる
const onSlideChange = () => {
  console.log('slide change');
};
</script>

<template>
  <div class="story-visuals">
    <swiper @swiper="onSwiper" @slideChange="onSlideChange">
      <swiperSlide v-for="(image, index) in images" :key="`slide-${index}`">
        <img :src="image" />
      </swiperSlide>
    </swiper>
  </div>
</template>

今回は拍子抜けするくらいあっさりと実装できました。

ナビゲーション、ページネーションを実装する

ナビゲーション、ページネーション機能の追加も簡単です。

<script setup lang="ts">
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Pagination, Virtual } from 'swiper';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';


</script>

<template>
  <div class="story-visuals">
    <swiper
      :modules="[Navigation, Pagination, Virtual]"
      navigation
      :pagination="{ clickable: true }"
      virtual
      @swiper="onSwiper"
      @slideChange="onSlideChange"
    >
      <swiperSlide v-for="(image, index) in images" :virtualIndex="index" :key="`slide-${index}`">
        <img :src="image" />
      </swiperSlide>
    </swiper>
  </div>
</template>

解説

import { Navigation, Pagination, Virtual } from 'swiper';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';

ナビゲーション、ページネーションのモジュール、それぞれの CSS を読み込んでいます。
Virtual を付けると描画が Vue.js を処理するように変更されるようです。

Virtual Slides rendering here is fully handled by Vue.js and not required anything except setting :virtual="true" property and setting virtualIndex on slides:

    <swiper
      :modules="[Navigation, Pagination, Virtual]"
      navigation
      :pagination="{ clickable: true }"
      virtual
      @swiper="onSwiper"
      @slideChange="onSlideChange"
    >
      <swiperSlide v-for="(image, index) in images" :virtualIndex="index" :key="`slide-${index}`">
        <img :src="image" />
      </swiperSlide>
    </swiper>

追加したモジュールを modules 属性で登録しています。
有効化のために navigationpaginationvirtual 属性を付与しています。
pagination 属性ではバレット(ページネーションの●)のクリック可を指定してます。

ナビゲーション、ページネーションのデザインをカスタマイズする

標準のCSSをコメントアウトして、独自CSSを追加しています。
設定が追加されて <swiper> が読みづらくなるので、設定を変数に書き出しました。
Virtual が有効だと上手く動作しないので外しました。

<script setup lang="ts">
import { Navigation, Pagination } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/css';

// 標準CSSは除外
// import 'swiper/css/navigation';
// import 'swiper/css/pagination';

// 〜略〜

const swiperModules = [Navigation, Pagination];

// ナビゲーションで使うエレメントのクラスを指定
const swiperNavigation = {
  prevEl: '.story-navi--prev',
  nextEl: '.story-navi--next',
};

// ページネーションで使うエレメントのクラスを指定
// 画像をサムネイルとして並べたいのでバレットの内容を指定
const swiperPagination = {
  el: '.story-thumbs',
  clickable: true,
  renderBullet: (index, className) =>
    `<span class="story-thumbs__item ${className} 1"><img src="${images[index]}" style="width:100%"></span>`,
};
</script>

<!-- スタイルシート -->
<style>
.story-visuals {
  width: 600px;
}
.story-thumbs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
<!-- ページネーションのアクティブ以外は透明度 0.4 -->
.story-thumbs__item {
  opacity: 0.4;
}
.story-thumbs__item.swiper-pagination-bullet-active {
  opacity: 1;
}
</style>

<template>
  <div class="story-visuals">
    <swiper
      :modules="swiperModules"
      :pagination="swiperPagination"
      :navigation="swiperNavigation"
      :loop="true"
      @swiper="onSwiper"
      @slideChange="onSlideChange"
    >
      <swiperSlide v-for="(image, index) in images" :key="`slide-${index}`">
        <img :src="image" />
      </swiperSlide>
    </swiper>

    <!-- サムネイル一覧 -->
    <div class="story-thumbs" />

    <!-- 左右移動 -->
    <span class="story-navi story-navi--prev">戻る</span>
    <span class="story-navi story-navi--next">進む</span>
  </div>
</template>

解説

const swiperPagination = {
  el: '.story-thumbs',
  clickable: true,
  renderBullet: (index, className) =>
    `<span class="story-thumbs__item ${className} 1"><img src="${images[index]}" style="width:100%"></span>`,
};

renderBullet はページネーションで使用するHTMLを記載します。
画像をサムネイルとして並べたかったので index で何番目かを取り出して画像取得に利用しています。

className にはアクティブなバレットのクラス swiper-pagination-bullet-active が入ります。

    <swiper
      :modules="swiperModules"
      :pagination="swiperPagination"
      :navigation="swiperNavigation"
      :loop="true"
      @swiper="onSwiper"
      @slideChange="onSlideChange"
    >

変数に書き出した設定を指定しています。
loop はカルーセルの無限ループです。

@swiper@slideChange はデバッグ用なので不要なら消してOKです。

おわり

Swiper は最初のとっかかりがわかりにくいのですが、そこを乗り越えれば大変便利なライブラリです。
この記事が一助になれば嬉しいです。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?