はじめに
高機能カルーセルライブラリといえば自分は Swiper をおすすめします。
Vue、React、Angularなどに対応し、カスタマイズ機能が豊富。
ただしバージョン毎にクセがあって、アップデートの度に導入トラブルで苦しめられていたのですが(自分の技量が足りない)、最新版の 8.0.5 ではスムーズに導入できたので備忘録として残します。
インストール
swiper
をインストールするだけです。
$ yarn add swiper
もし css-loader
、vue-style-loader
がなければインストールします。
$ yarn add --dev css-loader vue-style-loader
Webpack設定
この記事では Vue での開発環境が整っている前提ですので詳細は省きます。
webpack.config.js
の module.rules
に CSS設定を追加します。
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
属性で登録しています。
有効化のために navigation
、pagination
、virtual
属性を付与しています。
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 は最初のとっかかりがわかりにくいのですが、そこを乗り越えれば大変便利なライブラリです。
この記事が一助になれば嬉しいです。