Webサイトのコーディングをするなら必修とも言えるスライダー。
スライダー実装用のJSプラグインはいろいろありますが、本記事ではその中でもSwiperを使用した実用的なカスタマイズ例をご紹介します。
まずはこちらのデモページをご覧ください。
片方だけはみ出しているスライダー、サムネイルと連動するスライダー、SPだけスライダーにする方法などなど、実際によく必要になる機能を網羅できるようなデモを厳選してご用意しました。
初心者さん向けのかなり初歩的な内容から、いろいろカスタマイズを加えた応用編まで解説していきます。
メインビジュアルの作例5つ(#11 ~ #15)については、Swiperのカスタマイズ方法だけでなく、アニメーションやデザインの参考になればと思い様々なパターンで作成してみました。
アレンジがしやすいよう、いずれもコードが複雑になりすぎない範囲(のつもり)でカスタマイズしていますので、ぜひご覧ください。
本記事では Swiper v8.1.4(記事執筆時の最新)を使用しています。
Swiper 5系からはIEはサポート対象外となっています。IE対応が必要な場合は 4系 をご利用頂き、バージョンに合わせてオプションなどの書き換えを行う必要があります。
目次
- 1 Swiperってどんなプラグイン?
- 2 Swiperの導入方法
- 3 基本のHTML構造・クラス名
- 4 Swiperのオプション一覧
- 5 よく使うオプションを盛り込んだ初期化コードサンプル
- 6 #01 基本 基本のスライダー
- 7 #02 基本 右側だけはみ出た形のスライダー
- 8 #03 基本 コンテンツ幅からはみ出たスライドを薄くする
- 9 #04 応用 ブレークポイント切り替えでSPだけスライダーにする
- 10 #05 応用 総スライド数が slidesPerView を超える時だけスライダーにする
- 11 #06 応用 ゆっくり動き続ける無限ループスライダー
- 12 #07 基本 サムネイル(スライダー)を付けて連動させる
- 13 #08 応用 サムネイル(非スライダー)を付けて連動
- 14 #09 応用 スライダーの向きを交互に変える
- 15 #10 応用 スライダーを入れ子にする
- 16 #11 基本 メインビジュアル フェードアニメーション
- 17 #12 応用 始めと終わりで別々のアニメーションを付ける
- 18 #13 応用 縦方向のスライダー
- 19 #14 応用 スライドごとに表示時間を変えて、進捗状況と連動したプログレスバーを作成する
- 20 #15 応用 お手軽パララックス+スライド外のコンテンツと連動させる
- 21 同じページに同じスライダーを複数設置する方法
- 22 まとめ
Swiperってどんなプラグイン?
はじめに、Swiperの特徴を簡単にご紹介します。
Swiperのメリット
- ・jQuery非依存
- ・オプション・コールバック関数が豊富でカスタマイズ性が高い
- ・ドラッグ(スワイプ)したときの操作感が良い
- ・活発に開発されている
- ・利用者数が多く、日本語の参考記事も多い
SwiperはjQuery非依存なので、脱jQueryを求める方や、要件としてjQueryが使えないプロジェクトの場合はまず候補に挙がると思います。
また、オプション・コールバック関数が非常に多く用意されているのが特徴で、柔軟なカスタマイズが可能となっています。
そして個人的に気に入っているポイントが、「ドラッグ(スワイプ)したときの操作感が良い」というところ。
比較対象としてslickを挙げますが、slickで実装したスライダーにはドラッグでの移動量に制限があり、たくさんドラッグしても少ししか移動できない仕様です。
一方Swiperで実装したものは、ドラッグ量と移動量が自然に連動するストレスフリーな操作感になっています。スライド数が多い場合や、なるべくたくさん見てほしい!というときはSwiperのほうがお勧めです。
Swiperのデメリット
- ・たくさんの機能を把握するのが大変
- ・容量が大きい
- ・公式ドキュメントが英語
多彩な機能があるため全てを把握することが難しく、「このオプション使えばもっと簡単にできた…!」とあとで気付くということもしばしば。
そして、機能が多いだけあってファイル容量も大きいです。
(jQueryを読み込まないならその分軽い、と考えることもできますが)
※Webpackでバンドルする場合は必要なモジュールだけをインポートすることができるので、容量を削減できそうです。
Swiperの導入方法
続いて基本的な導入の流れをご説明します。
公式サイトで分かりやすく説明されているので、その流れに沿って簡単にまとめました。
01 Swiperのインストール
以下の3つの方法でインストールが可能です。
NPMでインストール
CDNを利用
ファイルをダウンロードして利用
詳しくは公式サイト Get Started をご確認ください。
とりあえず手軽に試してみたい、という方はCDNかファイルをダウンロードで進めてみてください。
本記事のデモではファイルをダウンロードして利用しています。
ファイルをダウンロードする場合
Download assets に記載されているリンク先(現在は v8)からCSSとJSをダウンロードします。
CSSが2種類あるのですが、読み込むのはどちらか一方だけでOKです。
swiper-bundle.min.css のほうだと何もCSSを書かなくてもある程度スライダーの形が出来上がるので、初心者さんはこちらのほうがやりやすいと思います。ただ、その分ファイル容量は大きくなります。
swiper.min.css のほうは装飾なしで必要最低限のスタイルのみになっているので、オリジナルデザインで実装するならこちらのほうが無駄が少ないです。
※本記事デモでは swiper.min.css を使用しています。
ダウンロードしたファイルをHTMLで読み込む
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Swiper DEMO</title> <link rel="stylesheet" href="path/swiper.min.css" /> </head> <body> CONTENTS <script src="path/swiper-bundle.min.js"></script> </body> </html> |
02 HTMLにスライド要素を追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!-- Slider main container --> <div class="swiper"> <!-- Additional required wrapper --> <div class="swiper-wrapper"> <!-- Slides --> <div class="swiper-slide">Slide 1</div> <div class="swiper-slide">Slide 2</div> <div class="swiper-slide">Slide 3</div> ... </div> <!-- If we need pagination --> <div class="swiper-pagination"></div> <!-- If we need navigation buttons --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> <!-- If we need scrollbar --> <div class="swiper-scrollbar"></div> </div> |
こちらが基本の作りです。
.swiper .swiper-wrapper .swiper-slide の3つは必須です。
.swiper-wrapper と .swiper-slide の間に他の要素を割り込ませたりするとうまく動かなくなるのでご注意ください。
その他の要素は必要なときだけ追加しましょう。
.swiper の中には他の独自コンテンツを追加しても大丈夫です。
それぞれの役割については次の 基本のHTML構造・クラス名 で説明しています。
03 JSでスライダーの初期化を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | const mySwiper = new Swiper('.swiper', { // Optional parameters loop: true, // If we need pagination pagination: { el: '.swiper-pagination', }, // Navigation arrows navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, // And if we need scrollbar scrollbar: { el: '.swiper-scrollbar', }, }); |
JSにて上記のようにスライダーの初期化を行います。
ここまで行うとスライダーが動き出します!
下で よく使うオプションを盛り込んだ初期化コードサンプル もご紹介していますのでご活用ください。
04 CSSでデザイン調整を行う
最後にCSSでデザインの調整をしたら実装完了となります。
本記事デモと同じ swiper.min.css を利用している場合、初期化しただけの状態だとページネーションなどが表示されません。
「上手く動いていないのかな?」と思う方もいるかもしれませんが、落ち着いてソースを確認し、生成されている要素に対してスタイルを当てていきましょう。
基本のHTML構造・クラス名
デザインやアニメーションをカスタマイズするときのために、基本構造をざっくり把握しておきましょう。
各クラス名はパラメータで指定すれば任意のクラス名に変えることもできますが、本記事では全てデフォルトのままという前提で説明しています。
.swiper | スライダー全体を囲むコンテナ(※バージョン6以前では .swiper-container )。 このエリア内にスライド要素が収まる形。 はみ出させたい場合は overflow: visible; で上書きします。 |
.swiper-wrapper | .swiper-slide(各スライド)を囲む要素。 これを transform で移動させることでスライダーの動きを実現しています。 .swiper-wrapper の直下には .swiper-slide だけを入れます。 |
.swiper-slide | この中に各スライドの要素を入れます。 デザインを調整する際、.swiper-slide に直接スタイルを当てると不具合に繋がる場合があるので要注意。 以下のように中身を囲う要素を作って、それに対してスタイルを当てるのがお勧めです。 <div class="swiper-slide"> <div class="item"> <img src="img.jpg" alt=""> <p class="text">テキストが入ります</p> </div> </div> |
.swiper-pagination | ページネーション |
.swiper-button-prev | .swiper-button-next | 前へ / 次へボタン |
.swiper-button-disabled | 前のスライド / 次のスライドがないときに付くクラス名 |
.swiper-scrollbar | スクロールバー |
.swiper > .swiper-wrapper > .swiper-slide の構造は守る必要があります。
.swiper-wrapper と .swiper-slide の間に他の要素を割り込ませたりするとうまく動かなくなるのでご注意ください。
それ以外の制限はあまりなく、ページネーション、前へ / 次へボタン、スクロールバーは .swiper の外に出しても問題なく動作します。
.swiper-slide-active | アクティブなスライドに付くクラス名。 ループモード時は状況によって .swiper-slide-duplicate-active が付く。 |
.swiper-slide-prev | アクティブなスライドの前のスライドに付くクラス名。 ループモード時は状況によって .swiper-slide-duplicate-prev が付く。 |
.swiper-slide-next | アクティブなスライドの次のスライドに付くクラス名。 ループモード時は状況によって .swiper-slide-duplicate-next が付く。 |
.swiper-slide-visible | ※ watchSlidesProgress: true のときのみ 表示状態のスライドに付くクラス名。 |
.swiper-slide-duplicate | ループモード時に複製されたスライドに付くクラス名。 |
こちらは現在のスライドの状況に応じて動的に追加されるクラス名です。
デザインやアニメーションのカスタマイズにはこのクラスを利用していきます。
ループモード時は「-duplicate」が間に入った別のクラスになる場合があるのでご注意ください。
詳しくは ループモード時の注意 で説明しています。
Swiperのオプション一覧
本記事で使用しているものを中心に、よく使うオプションをまとめました。
先にこれだけ読んでもよく分からないと思いますので、まずはデモを見てから、使用されているオプションを確認しに来て頂ければと思います。
パラメータ
公式サイトの一覧はこちら:Parameters
パラメータ | タイプ | デフォルト値 | 説明 |
---|---|---|---|
allowTouchMove | boolean | true | false:ドラッグ(スワイプ)でのスライド切り替えを無効にする。 |
autoplay | boolean | AutoplayOptions | ||
{ | |||
delay | boolean | 3000 | 次のスライドに切り替わるまでの時間を指定(ミリ秒)。 data-swiper-autoplay 属性を利用して、HTML側で個別に値を指定することも可能。 |
disableOnInteraction | boolean | true | true:ユーザーが操作したときに自動再生を止める。 false にしておくのがお勧め。 |
reverseDirection | boolean | false | 自動再生の進行方向を逆にする。 |
waitForTransition | boolean | true | true:スライド切り替えのアニメーションの間は自動再生を止める。 true だと1枚目のスライドだけ表示時間が短くなるため、表示時間を揃えたいなら false にするのがお勧め。 |
} | |||
breakpoints | object | ブレークポイントごとにパラメータを変更する。 < 例 > breakpoints: { // when window width is >= 320px 320: { slidesPerView: 2, spaceBetween: 20 }, // when window width is >= 640px 640: { slidesPerView: 4, spaceBetween: 40 } } | |
centeredSlides | boolean | false | true:アクティブなスライドが中央に来るようにする。 |
direction | ‘horizontal’ | ‘vertical’ | ‘horizontal’ | ‘vertical’:スライド方向を垂直方向にする。 |
effect | ‘slide’ | ‘fade’ | ‘cube’ | ‘coverflow’ | ‘flip’ | ‘creative’ | ‘cards’ | ‘slide’ | エフェクトのタイプを指定。 詳しくは 公式のデモ をご覧ください。 |
fadeEffect | FadeEffectOptions | ※ effect: ‘fade’ のときのみ | |
{ | |||
crossFade | boolean | false | true:スライドのクロスフェードを有効にする。 下のコンテンツが重なって出てしまわないように true がお勧め。 |
} | |||
followFinger | boolean | true | true:ドラッグ(スワイプ)と連動してスライダーが動き、前後のスライドが見えてくるようにする。 false:ドラッグ(スワイプ)中はスライダーを動かさない。アクティブなスライドにCSSアニメーションを適用するケースなどでは false のほうがお勧め。 |
freeMode | boolean | FreeModeOptions | false | true:自由にドラッグ(スワイプ)できるようにする。スライド位置がスナップしなくなる。 下の詳細設定が必要なければ freeMode: true だけでOK。 |
{ | |||
enabled | boolean | true:自由にドラッグ(スワイプ)できるようにする。スライド位置がスナップしなくなる。 | |
momentum | boolean | true | false:ドラッグ(スワイプ)した後の慣性スクロールをオフにする。 |
} | |||
grabCursor | boolean | false | true:PCでホバー時にマウスカーソルを「掴む」マークにする。 |
loop | boolean | false | true:無限ループさせる。 |
loopAdditionalSlides | number | 0 | ※ loop: true のときのみ 複製するスライド数を指定。0 だとループが滑らかに繋がらないことがあるため 1 以上がお勧め。 |
mousewheel | boolean | MousewheelOptions | true:マウスホイールによるスライド切り替えを有効にする。 下の詳細設定が必要なければ mousewheel: true だけでOK。 | |
{ | |||
forceToAxis | boolean | false | true:スライド方向とスクロール方向が一致しているときだけ有効にする。水平方向のスライダーなら水平方向のスクロールのみ、垂直方向のスライダーなら垂直方向のスクロールのみ可能となる。 |
invert | boolean | false | true:スクロール方向とスライドの動く向きを逆にする。 |
} | |||
nested | boolean | false | true:スライダーを入れ子にしているときにタッチイベントを正しく取得する。親子のスライド方向が同じ場合のみ使用。 |
pagination | boolean | PaginationOptions | ||
{ | |||
clickable | boolean | false | true:クリックでのスライド切り替えを有効にする。押せそうなデザインなら true にしておくのがお勧め。 |
type | ‘bullets’ | ‘fraction’ | ‘progressbar’ | ‘custom’ | ‘bullets’ | 表示タイプを指定。 ‘bullets’:スライド枚数分のドットが作られる。 ‘fraction’:分数で表示する(例:1 / 10)。 ‘progressbar’:スライドの進捗に応じてバーが伸びていく。 ‘custom’:出力内容をカスタマイズ。renderCustom で書式設定が必要。 |
renderBullet | function(index, className) | ※ type: ‘bullets’ のときのみ < 01、02…とスライド番号を入れたい場合の例 > renderBullet: (index, className) => { let num = ('00' + (index + 1)).slice(-2); return '' + num + ''; }, | |
renderCustom | function(swiper, current, total) | ※ type: ‘custom’ のときのみ 出力内容を指定。 | |
} | |||
roundLengths | boolean | false | true:スライドの横幅・高さの小数点以下を四捨五入して、中の画像や文字がぼやけないようにする。 |
slideToClickedSlide | boolean | false | true:クリックしたスライドに移動させる(アクティブにする)。 |
slidesPerView | number | ‘auto’ | 1 | 表示させるスライドの枚数を指定。 指定した枚数分がコンテナ内に収まるように調整される。 小数点以下の値も指定可能( 1.5 など)。 CSSでサイズ調整したい場合は ‘auto’ を指定。 |
spaceBetween | number | 0 | スライド間の余白を指定(px)。 CSSで margin を付けるとスライドの位置がずれていくことがあるため、こちらで指定するのが推奨。 |
speed | number | 300 | スライド切り替えのアニメーションのスピードを指定(ミリ秒)。 |
thumbs | ThumbsOptions | ||
{ | |||
swiper | Swiper | null | サムネイルのスライダーと連動させる場合、サムネイルのスライダーのSwiperインスタンス名を指定。 |
} | |||
watchSlidesProgress | boolean | false | true:各スライドの進捗状況を監視する。表示状態のスライドに .swiper-slide-visible のクラスが付くようになる。 |
プロパティ・メソッド
公式サイトの一覧はこちら:Methods & Properties
プロパティ
プロパティ | 説明 |
---|---|
swiper.activeIndex | アクティブなスライドのインデックス番号を取得。 |
swiper.realIndex | アクティブなスライドのインデックス番号を取得。 ※ループモードの場合は .activeIndex ではなくこちらを使用 |
swiper.params | 初期化時のパラメータの値を取得。 例:mySwiper.params.speed |
メソッド
メソッド | 説明 |
---|---|
swiper.destroy(deleteInstance, cleanStyles) | スライダーを無効化する。 deleteInstance:インスタンスを削除するかどうか(boolean)|デフォルトは true cleanStyles:スライダー要素の不要なスタイルを削除するかどうか(boolean)|デフォルトは true |
swiper.getTranslate() | スライダーの現在の translate の値を取得。 |
swiper.setTranslate(translate) | スライダーの translate の値を書き換える。 |
swiper.slideTo(index, speed, runCallbacks) | 指定したインデックス番号のスライドへ移動。 |
swiper.slideToLoop(index, speed, runCallbacks) | 指定したインデックス番号のスライドへ移動。 ※ループモードの場合は slideTo() ではなくこちらを使用 |
イベント
公式サイトの一覧はこちら:Events
スライド切り替え
似たような項目が多くてタイミングが分かりにくいので、自動再生をオンにしているときの発火する順でまとめてみました。スライド切り替えのたびに繰り返しこの順番で発火します。
このタイミングの差を利用すれば、思い通りのアニメーションを実装しやすいと思います。
※ユーザー操作によりスライドを切り替えたときや、フェードモードとスライドモードでは挙動に細かな違いが出ます。
発火順 | イベント名 | タイミング |
---|---|---|
1 | setTransition | アニメーションを開始するたび |
2 | setTranslate | .swiper-wrapper の位置(translate)が変わるたび |
3 | activeIndexChange | realIndexChange | スライドのインデックス番号が変わるとき ※ realIndexChange はループモード用 |
4 | slideChange | アクティブなスライドが変わるとき |
5 | beforeTransitionStart | アニメーション開始前 |
6 | transitionStart | アニメーション開始時 |
7 | slideChangeTransitionStart | スライド切り替えのアニメーション開始時 |
8 | setTransition | (1と同じ) |
9 | transitionEnd | アニメーション終了後 |
10 | slideChangeTransitionEnd | スライド切り替えのアニメーション終了後 |
初期化
発火順 | イベント名 | タイミング |
---|---|---|
1 | beforeInit | 初期化直前 |
自動再生を有効にしている場合はここで上記「スライド切り替え」の1~10が発火 | ||
2 | init | Swiperの初期化直後 |
3 | afterInit | 初期化直後 |
操作
イベント名 | タイミング |
---|---|
beforeResize | resize ハンドラの前 |
touchEnd | クリック後または指が離れたとき |
よく使うオプションを盛り込んだ初期化コードサンプル
前章のまとめとして、使用頻度が高いと思われるパラメータを一通り盛り込んだコードをご用意してみました!
実装の際にご活用頂ければと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | const mySwiper = new Swiper('.swiper', { effect: 'fade', // フェードモードにする(デフォルトは 'slide') fadeEffect: { crossFade: true, // クロスフェードを有効にする(フェードモードの場合 true 推奨) }, slidesPerView: 1, // コンテナ内に表示させるスライド数(CSSでサイズ指定する場合は 'auto') spaceBetween: 0, // スライド間の余白(px) centeredSlides: true, // アクティブなスライドを中央に配置する loop: true, // 無限ループさせる loopAdditionalSlides: 1, // 無限ループさせる場合に複製するスライド数 speed: 300, // スライドアニメーションのスピード(ミリ秒) autoplay: { // 自動再生させる delay: 3000, // 次のスライドに切り替わるまでの時間(ミリ秒) disableOnInteraction: false, // ユーザーが操作しても自動再生を止めない waitForTransition: false, // アニメーションの間も自動再生を止めない(最初のスライドの表示時間を揃えたいときに) }, grabCursor: true, // PCでマウスカーソルを「掴む」マークにする watchSlidesProgress: true, // スライドの進行状況を監視する pagination: { el: '.swiper-pagination', // ページネーション要素のクラス clickable: true, // クリックによるスライド切り替えを有効にする type: 'bullets' // 'bullets'(デフォルト) | 'fraction' | 'progressbar' }, navigation: { nextEl: '.swiper-button-next', // 「次へ」ボタン要素のクラス prevEl: '.swiper-button-prev', // 「前へ」ボタン要素のクラス }, scrollbar: { el: '.swiper-scrollbar', // スクロールバー要素のクラス }, thumbs { swiper: mySwiper2 // サムネイルのスライダーのインスタンス名 }, breakpoints: { // ブレークポイント 600: { // 画面幅600px以上で適用 slidesPerView: 2, }, 1025: { // 画面幅1025px以上で適用 slidesPerView: 4, spaceBetween: 32, } }, on: { // イベントを登録する slideChange: (swiper) => { console.log('Slide index changed to: ' + (swiper.realIndex + 1)); }, }, }); |
#01 基本 基本のスライダー
それではここから本題のデモの解説に入っていきます。
まずは最もよく見るタイプのごく普通のスライダーから。
前へ / 次へボタンだけがコンテンツ幅の外に出ているデザインって頻出ですよね。
よく見るタイプでありながら少し調整しにくいところもあるので、初心者さん向けにポイントとして挙げました。
デモのポイント
はみ出させたい要素があれば .swiper の外に出す
前へ / 次へボタンをスライドに対して上下中央揃えにする
ホバー効果でスライドを動かしたり、影を付けたりする場合の調整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | const mySwiper = new Swiper('.card01 .swiper', { slidesPerView: 1, spaceBetween: 24, grabCursor: true, pagination: { el: '.card01 .swiper-pagination', clickable: true, }, navigation: { nextEl: '.card01 .swiper-button-next', prevEl: '.card01 .swiper-button-prev', }, breakpoints: { 600: { slidesPerView: 2, }, 1025: { slidesPerView: 4, spaceBetween: 32, } }, }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <div class="card01 l-section"> <div class="l-inner"> <div class="swiper-area"> <div class="swiper"> <div class="swiper-wrapper"> <a href="#" class="swiper-slide"> <article class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/139/800/500" alt=""></div> <div class="slide-content"> <time class="slide-date" datetime="2021-12-01">2021.12.01</time> <h2 class="slide-title">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2> </div> </article> </a> ⋮ <a href="#" class="swiper-slide"> <article class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/127/800/500" alt=""></div> <div class="slide-content"> <time class="slide-date" datetime="2021-12-01">2021.12.01</time> <h2 class="slide-title">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2> </div> </article> </a> </div><!-- /swiper-wrapper --> </div><!-- /swiper --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div><!-- /swiper-area --> <div class="swiper-pagination"></div> </div> </div> |
はみ出させたい要素があれば .swiperの外に出す
前へ / 次へボタンをコンテンツ幅からはみ出させようとすると、.swiper に overflow: hidden; がかかっているため上手くいきません。
そういうときは .swiper の外に出してしまうのが一番簡単です。
position 調整のための親要素(ここでは .swiper-area )で囲っておき、.swiper-button-prev と .swiper-button-next を .swiper の外に出してしまえばシンプルに解決できます。
前へ / 次へボタンをスライドに対して上下中央揃えにする
前へ / 次へボタンはスライドの高さに対して上下中央揃えのデザインになっていることが多いと思います。
ですが、ページネーションがあるとその分の高さも含まれてしまいボタンの位置がずれてしまいます。
この場合でも .swiper の外に出してしまうのが早いです。
前へ / 次へボタンがいる階層よりも外に .swiper-pagination を出してしまえばOK。
ホバー効果でスライドを動かしたり、影を付けたりする場合の調整
スライドに影を付けていたり、ホバー効果でスライドの位置を動かしたりしている場合、そのままだと .swiper に overflow: hidden; がかかっているので切れてしまいます。
なので、はみ出させたい分だけ .swiper に padding を付けておくようにします。
本記事のデモでは、ホバー時にスライドを上に 16px 動かす効果を付けているので、.swiper に padding-top: 16px を付けています。
#02 基本 右側だけはみ出た形のスライダー
こちらもよく見るタイプのスライダー。
「スライダーの左端がコンテンツ幅に揃っていて、かつ右側だけはみ出ている」というデザインです。
また、デモ01では slidesPerView でスライドの横幅を制御していましたが、こちらではCSSのほうで固定値を指定するパターンをご紹介しています。
デモのポイント
初期状態で右側だけコンテンツ幅からはみ出る
スライドの横幅を固定値(px)にする
前へ / 次へボタンとページネーションを横並びにする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const mySwiper = new Swiper('.card02 .swiper', { slidesPerView: 'auto', spaceBetween: 16, grabCursor: true, pagination: { el: '.card02 .swiper-pagination', clickable: true, }, navigation: { nextEl: '.card02 .swiper-button-next', prevEl: '.card02 .swiper-button-prev', }, breakpoints: { 1025: { spaceBetween: 32, } }, }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <div class="card02 l-section"> <div class="l-inner"> <div class="swiper"> <div class="swiper-wrapper"> <a href="#" class="swiper-slide"> <article class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/139/800/500" alt=""></div> <div class="slide-content"> <time class="slide-date" datetime="2021-12-01">2021.12.01</time> <h2 class="slide-title">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2> </div> </article> </a> ⋮ <a href="#" class="swiper-slide"> <article class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/127/800/500" alt=""></div> <div class="slide-content"> <time class="slide-date" datetime="2021-12-01">2021.12.01</time> <h2 class="slide-title">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2> </div> </article> </a> </div><!-- /swiper-wrapper --> <div class="swiper-controller"> <div class="swiper-pagination"></div> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div> </div><!-- /swiper --> </div> </div> |
初期状態で右側だけコンテンツ幅からはみ出る
左だけ余白が付いていればいいから横幅は100%にして margin-left を付けて…のような調整をし始めると、あまり上手くいきません。
「基準位置はコンテンツ幅にしつつ、そこからはみ出させたい」ときは、.swiper にかかっている overflow: hidden; を visible で上書きしましょう。そして代わりに親要素のほうに overflow: hidden; をかけておくようにします。
1 2 3 4 5 6 | .card02 { overflow: hidden; // 2.代わりに親要素で hidden にする .swiper { overflow: visible; // 1.はみ出させるように visible で上書き } } |
スライドの横幅を固定値(px)にする
slidesPerView でスライド数を指定すると、コンテナのサイズに応じてスライドのサイズも動的に変わる形になります。
そうではなく「どの画面幅でも常に○pxにしたい」という場合は、slidesPerView: ‘auto’ を指定することで、CSSでのサイズ調整が可能になります。
1 2 3 4 | const mySwiper = new Swiper('.card02 .swiper', { slidesPerView: 'auto', ⋮ }); |
1 2 3 4 5 | .card02 { .swiper-slide { width: 36rem; } } |
前へ / 次へボタンとページネーションを横並びにする
前へ / 次へボタンやページネーションのHTML構造は自由に変えられるので、横並びにしたければこの2つを囲う div(ここでは .swiper-controller )を追加しましょう。
あとはCSSで flex をかけるなど適宜調整するだけ。
1 2 3 4 5 6 7 8 9 10 | <div class="swiper"> <div class="swiper-wrapper"> ⋮ </div><!-- /swiper-wrapper --> <div class="swiper-controller"> <div class="swiper-pagination"></div> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div> </div><!-- /swiper --> |
1 2 3 4 5 6 7 8 9 10 11 12 | .card02 { .swiper-controller { display: flex; gap: 1.6rem; align-items: center; justify-content: flex-end; margin-top: 3.2rem; } .swiper-pagination { margin-right: auto; } } |
#03 基本 コンテンツ幅からはみ出たスライドを薄くする
デモ01をベースに、追加で3点のカスタマイズを行ったパターンです。
いずれも基本のオプションのみで簡単に実装できます。
デモのポイント
自動再生させる
無限ループさせる
コンテンツ幅からはみ出たスライドを薄くする
自動再生させる
1 2 3 4 5 6 7 8 9 | const mySwiper = new Swiper('.card03 .swiper', { ⋮ speed: 1000, autoplay: { delay: 4000, disableOnInteraction: false, }, ⋮ }); |
speed:スライドアニメーション(切り替わるときの動き)のスピードをミリ秒で指定します。
autoplay.delay:自動的に次のスライドに切り替わるまでの時間(間隔)をミリ秒で指定します。
autoplay.disableOnInteraction:false にすればユーザーがドラッグなどの操作をしても自動再生が止まらないようになります。
無限ループさせる
1 2 3 4 5 6 | const mySwiper = new Swiper('.card03 .swiper', { ⋮ loop: true, loopAdditionalSlides: 1, ⋮ }); |
loop:true にするとスライダーが無限ループするようになります。
loopAdditionalSlides:ループモード時に複製するスライド数を指定します。未指定だと滑らかに繋がらないことがあるため、基本的に1以上で設定しておくのがお勧め。
ループモード時に気を付けること
ループモードにすると、最初と最後のスライドがつながるようにスライドが複製されます。
.swiper-slide の数が元々のHTMLよりも増える形になるので、JSでindexを取得して何か処理をしようとするとうまくいきません。
アクティブなスライドのインデックス番号を取りたいときは、swiper.realIndex を利用しましょう。
また、基本のHTML構造・クラス名 で説明している通り、状況によって .swiper-slide-active .swiper-slide-prev .swiper-slide-next の間に「-duplicate」が入った別のクラス名が付くことがあります。
その場合はCSSを以下のように書いているとスタイルが当たりません。
.swiper-slide-prev { … }
.swiper-slide-next { … }
このままで問題ないケースも多いのですが、後半で紹介しているメインビジュアルの作例ではアニメーションのつながりが一部おかしくなってしまいます。
そういうときは以下のように書き方を変更してみましょう。
.swiper-slide[class*=-prev] { … }
.swiper-slide[class*=-next] { … }
コンテンツ幅からはみ出たスライドを薄くする
1 2 3 4 5 | const mySwiper = new Swiper('.card03 .swiper', { ⋮ watchSlidesProgress: true, ⋮ }); |
1 2 3 4 5 6 7 8 9 10 | .card03 { .swiper-slide { &:not(.swiper-slide-visible) { .slide { pointer-events: none; opacity: .3; } } } } |
表示状態のスライド( .swiper のエリア内にいるもの)を判定するために、watchSlidesProgress を利用します。こちらを true にすると、表示状態のスライドに .swiper-slide-visible のクラスが付くようになります。
あとはCSSで .swiper-slide-visible 以外のスライドを薄くしておきます。
#04 応用 ブレークポイント切り替えでSPだけスライダーにする
SPだけスライダーにするのでPCのときは swiper.destroy() をすればよい!ということはすぐ分かると思うのですが、きちんと「PC↔SPの切り替え時にスライダーの解除↔再初期化をする」ためには「スライダーが初期化済みかどうか」の判定を入れることがポイントです。
デモのポイント
ブレークポイント通過時に実行する
スライダーの解除↔再初期化を行う
スライダーが初期化されていないときのスタイル調整を行う
ブレークポイント通過時に実行する
1 2 3 4 5 6 7 8 9 10 11 12 | const mediaQuery = window.matchMedia('(max-width: 1024px)'); // メディアクエリを登録 const checkBreakpoint = (e) => { if (e.matches) { // メディアクエリにマッチしたとき(1024px 以下)の処理 } else if (mySwiper) { // メディアクエリにマッチしなかったとき(1025px 以上)の処理 } } mediaQuery.addListener(checkBreakpoint); // ブレークポイント通過時に実行 checkBreakpoint(mediaQuery); // ロード時に初回実行 |
ブレークポイントの判定には window.matchMedia と addListener() を用い、mediaQuery に変化があったとき(ブレークポイントを通過したとき)だけ実行させるようにしています。
ただし、このコードだとブレークポイントが複数あるケースに対応できません。その場合は 次のデモ #05 のほうの書き方をしてみてください。
スライダーの解除↔再初期化を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | let mySwiper = null; const checkBreakpoint = (e) => { // メディアクエリにマッチしたらスライダーを初期化 if (e.matches) { initSwiper(); } else if (mySwiper) { // メディアクエリにマッチせず、スライダーが初期化済みだったらスライダーを解除 mySwiper.destroy(false, true); } } const initSwiper = () => { mySwiper = new Swiper('.card04 .swiper-a', { ⋮ }); }; |
続いて、メディアクエリにマッチしたらスライダーを初期化し、マッチしなかったらスライダーを解除する、という処理を追加します。
スライダーを解除する処理は、if (mySwiper) でスライダーが初期化済みのときだけ実行されるようにします。そのため mySwiper の初期値は null にしておきましょう。
スライダーが初期化されていないときのスタイル調整を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .card04 { /* スライダー非活性時 */ .swiper-a:not(.swiper-initialized) { padding: 0; .swiper-button-prev, .swiper-button-next { display: none; } .swiper-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; } } } |
PCのときはスライダーが生成されていない状態なので、横並びにするなどのレイアウト調整はCSSで行う必要があります。
また、SPからPCに切り替えると、前へ / 次へボタンやページネーションなどの一度生成された要素はそのまま残るので、display: none; にしておきましょう。
#05 応用 総スライド数が slidesPerView を超える時だけスライダーにする
先ほどのデモは、ブレークポイント(1点)によってスライダーの解除↔初期化を切り替えるシンプルな仕様でした。
一方こちらは、ブレークポイント(複数)ごとに総スライド数と slidesPerView を比較して、総スライド数の方が多いときだけスライダーにする、という形で作成してみました。
こちらの方がより実際のニーズに応えられる仕様ではないでしょうか。
予めブレークポイントと slidesPerView のセットを配列に格納しておき、総スライド数と比較してスライダーの解除↔初期化を切り替えるという形をとっています。
ブレークポイントがさらに多い場合も、Swiperのパラメータ(breakpoints)と配列にそれぞれ追加すれば対応できます。
デモのポイント
ブレークポイントと slidesPerView のセットを配列に格納
スライダーの解除↔再初期化を行う
スライダーが初期化されていないときのスタイル調整を行う
ブレークポイントと slidesPerView のセットを配列に格納
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // ブレークポイントごとの slidesPerView を配列に格納 const breakpoints = [ { breakpoint: 0, slidesPerView: 1, }, { breakpoint: 600, slidesPerView: 2, }, { breakpoint: 1025, slidesPerView: 4, } ]; breakpoints.reverse(); // 配列を逆順にする |
まずブレークポイントと slidesPerView のセットを配列に格納しておきます。
breakpoints.reverse() をしているのは、Swiperのパラメータ(breakpoints)の指定順と、配列の順番を揃えたかったためです。
スライダーの解除↔再初期化を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | let mySwiper, mySlidesPerView; let isInited = false; // スライダーが初期化済みかどうかのフラグ const slideLength = document.querySelectorAll('.card04 .swiper-b .swiper-slide').length; // 総スライド数を取得 const checkBreakpoint = () => { for (let key in breakpoints) { let bp = breakpoints[key]['breakpoint']; // 大きいブレークポイントから順に、メディアクエリにマッチするか判定 if (window.matchMedia('(min-width:' + bp + 'px)').matches) { // マッチする場合、そのブレークポイントの slidesPerView をセット mySlidesPerView = breakpoints[key]['slidesPerView']; break; } } if (slideLength > mySlidesPerView) { if (!isInited) { // 総スライド数が slidesPerView より多く、スライダーがまだ初期化されていなかったら、スライダーを初期化 initSwiper(); isInited = true; } } else { if (isInited) { // 総スライド数が slidesPerView より少なく、スライダーが初期化済みだったら、スライダーを解除 mySwiper.destroy(false, true); isInited = false; } } }; const initSwiper = () => { mySwiper = new Swiper('.card04 .swiper-b', { ⋮ }); }; window.addEventListener('resize', function() { checkBreakpoint(); // リサイズ時に実行 }); checkBreakpoint(); // ロード時に初回実行 |
ロード時とリサイズ時に、配列のブレークポイントがメディアクエリにマッチするか判定していき、マッチしたブレークポイントの slidesPerView と総スライド数を比較して解除↔再初期化を切り替えます。
スライダーが初期化されていないときのスタイル調整を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | .card04 { /* スライダー非活性時 */ .swiper-b:not(.swiper-initialized) { padding: 0; .swiper-button-prev, .swiper-button-next { display: none; } .swiper-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; @media only screen and (max-width: 1024px) and (min-width: 600px) { grid-template-columns: repeat(2, 1fr); gap: 24px; } @media only screen and (max-width: 599px) { grid-template-columns: repeat(1, 1fr); } } } } |
先ほどのデモと同様に、スライダーが生成されていないときのレイアウト調整をCSSで行います。
#06 応用 ゆっくり動き続ける無限ループスライダー
通常の自動再生は「スライダーが止まる→動く」の繰り返しですが、止まらずに常にゆっくり動き続けるという形にしたいときもありますよね。
基本的には autoplay.delay を 0 にすれば実現できるのですが、そのままだとユーザーがドラッグしたときの挙動がおかしな感じになってしまうので、そこをカスタマイズする必要があります。
※ユーザーがドラッグできない形でよければ、allowTouchMove: false にしておくだけでOKです。
デモのポイント
ゆっくり動き続けるようにする
ユーザーがドラッグしたときのスピード調整
ホバーしたら他のスライドを薄くする
ゆっくり動き続けるようにする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const initSwiper = () => { const mySwiper = new Swiper('.card05 .swiper', { ⋮ speed: 8000, autoplay: { delay: 0, }, ⋮ }); }; window.addEventListener('load', function(){ initSwiper(); // ページ読み込み後に初期化 }); |
autoplay.delay を 0 にすることで常に動き続ける形にすることができます。
注意点
初期化のタイミングをページ読み込み後にしておかないと、自動再生が止まった状態で表示されてしまうことがありました。
ユーザーがドラッグしたときのスピード調整
1 2 3 4 5 6 7 8 9 10 11 12 13 | const mySwiper = new Swiper('.card05 .swiper', { ⋮ freeMode: { enabled: true, momentum: false, }, ⋮ on: { touchEnd: (swiper) => { swiper.slideTo(swiper.activeIndex + 1); } } }); |
ユーザーのドラッグ操作が終わったとき( touchEnd )に、 slideTo(swiper.activeIndex + 1) で次のスライドへ移動させるようにしました。
また、スライド位置のスナップや慣性スクロールが邪魔になるため、以下の設定も重要です。
freeMode:true にするとスライド位置がスナップしなくなります。
freeMode.momentum:false にすると慣性スクロールがオフになります。
こちらの記事を参考にさせていただきました。
スワイプできる無限ループスライダーを実装する【Swiper】
ホバーしたら他のスライドを薄くする
1 2 3 4 5 6 7 8 9 10 | .card05 { /* スライド ホバー時 */ .swiper:hover { .slide:not(:hover) { @media only screen and (min-width: 1025px) { opacity: .3; } } } } |
ホバー中の要素だけを目立たせることで、注目を引くことができる表現ですね。
このように手軽に実装することができます。
#07 基本 サムネイル(スライダー)を付けて連動させる
メインのスライダーとサムネイルのスライダーがあり、2つを連動させるタイプです。
非常に簡単に実装でき、そのままでサムネイルのスライダーの動きも自然なので、複雑なカスタマイズは必要ありません。
また、このデモで初めてフェードモードを使用しているので、フェードモードのときに共通で調整しておきたい項目も合わせてご説明します。
デモのポイント
2つのスライダーを連動させる
フェードモード時の共通調整
2つのスライダーを連動させる
1 2 3 4 5 6 7 8 9 10 | const mySwiper_thumb = new Swiper('.gallery01 .swiper-thumb', { ⋮ }); const mySwiper_main = new Swiper('.gallery01 .swiper-main', { ⋮ thumbs: { swiper: mySwiper_thumb, }, }); |
thumbs で連動させたいスライダーのインスタンス名(ここでは mySwiper_thumb )を指定すると、2つのスライダーが連動するようになります。
mySwiper_main のほうで mySwiper_thumb を指定するので、初期化の記述順が逆だとエラーになってしまいますのでご注意ください。
フェードモード時の共通調整
1 2 3 4 5 6 7 | const mySwiper_main = new Swiper('.gallery01 .swiper-main', { effect: 'fade', fadeEffect: { crossFade: true, }, ⋮ }); |
切り替えのアニメーションをスライドではなくフェードにするときは、このように指定します。
注意点
フェードモードにするとすべてのスライドが重なる形になります。もしスライド内に透明の余白があった場合、下のスライドが見えてしまいます。
対策として fadeEffect.crossFade: true もセットで指定しておきましょう。
注意点
フェードモード時はドラッグ操作ではふわっと切り替わるのですが、前へ / 次へボタンを押すと transition が効かずにパッと切り替わってしまいます。
対策として、以下のようにCSSで上書きしておくのがお勧めです。
1 2 3 4 5 6 | /* フェードモード時 共通調整 */ .swiper-fade { .swiper-slide { transition-property: opacity !important; } } |
#08 応用 サムネイル(非スライダー)を付けて連動
こちらはサムネイルをスライダーにしないで連動させるタイプです。
thumbs は使わずに、スライド切り替え時にアクティブ用のクラスを付け替える処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | const thumb = document.querySelectorAll('.gallery02 .thumb-media'); const switchThumb = (index) => { document.querySelector('.gallery02 .thumb-media-active').classList.remove('thumb-media-active'); thumb[index].classList.add('thumb-media-active'); } const mySwiper = new Swiper('.gallery02 .swiper', { ⋮ on: { afterInit: (swiper) => { thumb[swiper.realIndex].classList.add('thumb-media-active'); for (let i = 0; i < thumb.length; i++) { thumb[i].onclick = () => { swiper.slideTo(i); }; } }, slideChange: (swiper) => { switchThumb(swiper.realIndex); }, } }); |
サムネイルはスライダーにしないので、普通にCSSでレイアウト調整をしておきます。
そしてメイン画像のスライダーのほうで、スライドが切り替わったとき( slideChange )に、アクティブなスライド番号を取得し、対応するサムネイルにクラス .thumb-media-active を付与する、という処理を追加します。
また、予めHTML側でアクティブ状態のサムネイルにクラス .thumb-media-active を付けておき、スライダーの初期化時( afterInit )にその順番を取得して表示を合わせるようにしました。
#09 応用 スライダーの向きを交互に変える
#06 ゆっくり動き続ける無限ループスライダー からの派生で、進行方向を交互に変えたパターンです。
同じオプションはまとめておき、別々に設定する箇所を後から追加する形にしました。
デモのポイント
スライダーの進行方向を逆にする
同じオプションをまとめる
スライダーの進行方向を逆にする
1 2 3 | autoplay: { reverseDirection: true }, |
autoplay.reverseDirection:true にすると自動再生の進行方向が逆向きになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 進行方向:通常 on: { touchEnd: (swiper) => { swiper.slideTo(swiper.activeIndex + 1); } } // 進行方向:逆向き on: { touchEnd: (swiper) => { swiper.slideTo(swiper.activeIndex - 1); } } |
#06 ユーザーがドラッグしたときのスピード調整 と同様ですが、方向が逆向きの場合は slideTo(swiper.activeIndex – 1); となるようにしているところがポイント。
同じオプションをまとめる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | const params = { slidesPerView: 'auto', loop: true, loopedSlides: slideLength, speed: 6000, autoplay: { delay: 0, disableOnInteraction: false, }, freeMode: { enabled: true, momentum: false, }, grabCursor: true, } const initSwiper = () => { const mySwiper_a = new Swiper('.gallery03 .swiper-a', { ...params, on: { touchEnd: (swiper) => { swiper.slideTo(swiper.activeIndex + 1); } } }); const mySwiper_b = new Swiper('.gallery03 .swiper-b', { ...params, autoplay: { ...params.autoplay, reverseDirection: true }, on: { touchEnd: (swiper) => { swiper.slideTo(swiper.activeIndex - 1); } } }); }; window.addEventListener('load', function(){ initSwiper(); // ページ読み込み後に初期化 }); |
まず params で共通のオプションをまとめておき、異なる部分はスプレッド構文( … )を利用して後から追加しています。
#10 応用 スライダーを入れ子にする
手順説明などで見かける、スライダーが入れ子になっているパターンです。
また、「STEP.01」のようにページネーションの出力内容をカスタマイズしてみました。
デモのポイント
スライダーを入れ子にする
ページネーションの出力内容をカスタマイズする
スライダーを入れ子にする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const mySwiper_sub = new Swiper('.flow01 .swiper-sub', { ⋮ nested: true, pagination: { el: '.flow01 .swiper-pagination-sub', }, }); const mySwiper_main = new Swiper('.flow01 .swiper-main', { ⋮ pagination: { el: '.flow01 .swiper-pagination-main', }, navigation: { nextEl: '.flow01 .swiper-button-next', prevEl: '.flow01 .swiper-button-prev', }, ⋮ }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | <div class="flow01 l-section"> <div class="l-inner"> <div class="swiper-pagination-main"></div> <div class="swiper swiper-main"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="mainslide"> <h2 class="mainslide-title">Lorem ipsum</h2> <div class="swiper swiper-sub"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="subslide"> <div class="subslide-media"><img src="./img/icon01.svg" alt=""></div> <p class="subslide-text">Dolor sit amet, consectetur adipiscing elit.</p> </div> </div> ⋮ <div class="swiper-slide"> <div class="subslide"> <div class="subslide-media"><img src="./img/icon03.svg" alt=""></div> <p class="subslide-text">Ad minim veniam.</p> </div> </div> </div><!-- /swiper-wrapper --> <div class="swiper-pagination-sub"></div> </div><!-- /swiper-sub --> </div> </div> <div class="swiper-slide"> <div class="mainslide"> <h2 class="mainslide-title">Sed eiusmod</h2> <div class="subslide"> <div class="subslide-media"><img src="./img/icon05.svg" alt=""></div> <p class="subslide-text">Dolor sit amet, consectetur adipiscing elit.</p> </div> </div> </div> <div class="swiper-slide"> <div class="mainslide"> <h2 class="mainslide-title">Ut enim</h2> <div class="swiper swiper-sub"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="subslide"> <div class="subslide-media"><img src="./img/icon01.svg" alt=""></div> <p class="subslide-text">Dolor sit amet, consectetur adipiscing elit.</p> </div> </div> ⋮ <div class="swiper-slide"> <div class="subslide"> <div class="subslide-media"><img src="./img/icon04.svg" alt=""></div> <p class="subslide-text">Ad minim veniam.</p> </div> </div> </div><!-- /swiper-wrapper --> <div class="swiper-pagination-sub"></div> </div><!-- /swiper-sub --> </div> </div> </div><!-- /swiper-wrapper --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div><!-- /swiper-main --> </div> </div> |
このように、親スライダーと子スライダーをそのまま入れ子構造にするだけで実装できます。分かりやすいですね。
親スライダーのほうに指定している nested: true が重要で、親と子のスライダーの方向が同じ場合の誤作動を防ぐための設定です。もしこの設定をしていないと、子スライダーをドラッグしたいのに親スライダーが動いてしまう!といった問題が起こりますのでご注意ください。
ページネーションの出力内容をカスタマイズする
1 2 3 4 5 6 7 8 9 10 11 | const mySwiper_main = new Swiper('.flow01 .swiper-main', { ⋮ pagination: { ⋮ renderBullet: (index, className) => { let num = ('00' + (index + 1)).slice(-2); return '<span class="' + className + '"><span class="step">STEP.</span>' + num + '</span>'; }, }, ⋮ }); |
renderBullet でページネーションの内容をカスタマイズすることができます。
index でスライド番号、className でクラス名を取得できるので、それを利用しつつ「STEP.01」の形になるように調整してみました。
#11 基本 メインビジュアル フェードアニメーション
写真がゆっくりズームインし、テキストが少し遅れてフェードインする、というシンプルなアニメーションを付けてみました。このくらいであれば特別なカスタマイズは不要で、フェードモードで実装したものに対しCSSでアニメーションを付けるだけで作成できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const mySwiper = new Swiper('.mv01 .swiper', { effect: 'fade', fadeEffect: { crossFade: true, }, loop: true, loopAdditionalSlides: 1, speed: 2000, autoplay: { delay: 7000, disableOnInteraction: false, waitForTransition: false, }, followFinger: false, pagination: { el: '.mv01 .swiper-pagination', clickable: true, }, }); |
autoplay.waitForTransition:false にすると、スライド切り替え中にも自動再生が止まらないようになります。true だと最初のスライドの表示時間だけ短くなってしまうので、本デモのようにアニメーション演出を入れるなら false にしておいたほうが自然だと思います。
followFinger:false にすると、スライド切り替えのアニメーションはドラッグ操作が終了してから開始されるようになります。テキストや背景にアニメーションを付けているので、ドラッグ中に下のスライドがだんだん透けて出てきてしまうと困るため、false にしています。
フェードモード時の共通の注意事項は フェードモード時の共通調整 でも説明していますので、合わせてご確認ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | .mv01 { /* スライド アクティブ時 */ .swiper-slide { &[class*=-active] { .slide-media img { transition-delay: 0s; transform: scale(1.05); } .slide-title { animation: mv01-fadeIn 2s .5s var(--easing) both; } } } } @keyframes mv01-fadeIn { 0% { transform: scale(.5); opacity: 0; filter: blur(300px); } 100% { transform: scale(1); opacity: 1; filter: blur(0); } } |
[class*=-active] のように指定することで、クラスに -active が含まれる場合にアニメーションを適用させています。
#12 応用 始めと終わりで別々のアニメーションを付ける
アクティブになったときはコンテンツが下から上にフェードインして、次のスライドに切り替わる際にはさらに上に移動しながらフェードアウトする、という感じで違うアニメーションを組み合わせてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | const myDelay = 7000; let timer; // アニメーション切り替え const switchAnimation = () => { clearTimeout(timer); let activeSlide = document.querySelectorAll('.mv02 .swiper-slide[class*=-active]'); for (let i = 0; i < activeSlide.length; i++) { activeSlide[i].classList.remove('anm-finished'); activeSlide[i].classList.add('anm-started'); } timer = setTimeout(() => { for (let i = 0; i < activeSlide.length; i++) { activeSlide[i].classList.remove('anm-started'); activeSlide[i].classList.add('anm-finished'); } }, myDelay - 1000); } // アニメーション終了 const finishAnimation = () => { let activeSlide = document.querySelectorAll('.mv02 .swiper-slide.anm-started'); for (let i = 0; i < activeSlide.length; i++) { activeSlide[i].classList.remove('anm-started'); activeSlide[i].classList.add('anm-finished'); } } const mySwiper = new Swiper('.mv02 .swiper', { ⋮ on: { slideChange: () => { finishAnimation(); }, slideChangeTransitionStart: () => { switchAnimation(); }, } }); |
.anm-started が始めのアニメーション用(下から上にフェードイン)
.anm-finished が終わりのアニメーション用(上に移動しながらフェードアウト)
のクラス名です。
1:slideChangeTransitionStart のときにアクティブなスライドに .anm-started を追加
2:myDelay – 1000 の時間がたったら .anm-started を .anm-finished に付け替える
3:slideChange のときに .anm-started を .anm-finished に付け替える
※ユーザーが手動でスライドを切り替えたときのための処理です
このようにクラスを付け替えて、ちょうどよいタイミングでアニメーションがかかるようにしています。
#13 応用 縦方向のスライダー
こちらはスライダーの方向を縦にしてみたパターンです。
横方向スライダーとの違いは .swiper への高さ指定が必須になることくらいで、他には方向が変わることによる特別な調整は必要ありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | let mySwiper_main; const mySwiper_thumb = new Swiper('.mv03 .swiper-thumb', { ⋮ direction: 'vertical', slideToClickedSlide: true, thumbs: { swiper: mySwiper_main, }, }); mySwiper_main = new Swiper('.mv03 .swiper-main', { ⋮ direction: 'vertical', thumbs: { swiper: mySwiper_thumb, }, }); |
direction:スライダーの方向を指定します。’vertical’ で縦方向になります。
slideToClickedSlide:true にすると、クリックしたスライドまで移動する(アクティブにする)ようになります。
サムネイルスライダーの連動についてですが、#07 のほうでは片方だけを連動させればよかったのに対し、このデモでは双方向に連動させる必要があります。そのため、thumbs で指定するインスタンス名がエラーになってしまわないように、最初に let mySwiper_main; で宣言しておくようにしました。
1 2 3 4 5 6 7 8 | .mv03 { .swiper-main { height: 800px; @media only screen and (max-width: 1024px) { height: max(70vh, 70vw); } } } |
縦方向の場合は .swiper への高さ指定が必須となります。
#14 応用 スライドごとに表示時間を変えて、進捗状況と連動したプログレスバーを作成する
スライドによってテキスト量が全然違うケースなどでは、スライドごとに表示時間を変える必要が出てくると思います。そうなると、プログレスバーなどのアニメーションもスライド表示時間とぴったり合うようにしたいですよね。
本デモではその2点のカスタマイズをご紹介します。
プログレスバーはもともとページネーションで用意されているのですが、最後のスライドから最初のスライドに戻ったときの動きなどがちょっと理想と違う部分があり、別で作成しました。
デモのポイント
スライドごとに表示時間(delay)を変える
進捗状況と連動したアニメーション(プログレスバー)
スライドごとに表示時間(delay)を変える
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <div class="swiper"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/139/2400/1500" alt=""></div> <div class="slide-content"> <div class="slide-fraction"></div> <h2 class="slide-title">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2> </div> </div> </div> <div class="swiper-slide" data-swiper-autoplay="14000"> <div class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/1060/2400/1500" alt=""></div> <div class="slide-content"> <div class="slide-fraction"></div> <h2 class="slide-title">Sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur.</h2> </div> </div> </div> ⋮ |
autoplay.delay の値はHTML側でも指定できるようになっています。
上記サンプルのように、.swiper-slide に data-swiper-autoplay の属性でミリ秒で指定します。
進捗状況と連動したアニメーション(プログレスバー)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | const myDelay = 7000; const slide = document.querySelectorAll('.mv04 .swiper-slide'); const slideLength = slide.length; // スライド番号・円形ライン用のSVGを追加 const fraction = document.querySelectorAll('.mv04 .slide-fraction'); const fractionEl = '<span class="circle"><svg viewBox="0 0 64 64"><circle class="circle-01" cx="50%" cy="50%" r="31"></circle><circle class="circle-02" cx="50%" cy="50%" r="31"></circle></svg></span><span class="current"></span><span class="total">' + slideLength + '</span>'; for (let i = 0; i < fraction.length; i++) { fraction[i].insertAdjacentHTML('beforeend', fractionEl); } // 各スライドの表示時間とアニメーションを連動 const slideDelay = []; for (let i = 0; i < slideLength; i++) { // HTML側の data-swiper-autoplay を取得 let delay = Number(slide[i].getAttribute('data-swiper-autoplay')); slideDelay[i] = delay ? delay : myDelay; // HTMLに指定がなければ myDelay をセット // 各タグに transitionDuration / animationDuration をセット slide[i].querySelector('img').style.transitionDuration = slideDelay[i] + 'ms'; slide[i].querySelector('.circle-02').style.animationDuration = slideDelay[i] + 'ms'; slide[i].querySelector('.current').textContent = i + 1; } // プログレスバーの更新処理 const progressBar = document.querySelector('.mv04 .progressbar-fill'); const updateProgress = (index) => { let start = Math.floor(index / slideLength * 100) / 100; let end = Math.floor((index + 1) / slideLength * 100) / 100; // 一度スタートの位置に移動してから progressBar.style.transition = ''; progressBar.style.transform = 'scaleX(' + start + ')'; setTimeout(() => { // ゴールの位置まで伸ばす progressBar.style.transition = slideDelay[index] + 'ms linear'; progressBar.style.transform = 'scaleX(' + end + ')'; }, 100); } const mySwiper = new Swiper('.mv04 .swiper', { ⋮ on: { afterInit: () => { updateProgress(0); }, realIndexChange: (swiper) => { updateProgress(swiper.realIndex); }, } }); |
下のプログレスバー( progressbar )はスライダー全体の進捗率、円形ライン( .slide-fraction )は各スライドの進捗率と連動しています。
アニメーションはすべてCSSで作成し、transitionDuration と animationDuration をそれぞれの要素にセットすることで時間を合わせる形となっています。
#15 応用 お手軽パララックス+スライド外のコンテンツと連動させる
メインビジュアルのように画面いっぱいに広がるデザインでは、スライドモードを用いると少しうるさく感じてしまうので、フェードモードを採用しがちです。
でもちょっとしたひと工夫でスライドモードならではの表現を楽しむこともできます!ということで、このデモでは横にずれる効果を利用した視差効果(パララックス)を付けてみました。
こちら非常に簡単に対応できるので、ちょっとしたクオリティアップにお勧めです。
また、スライドするのは背景だけにして、その上に位置固定でコンテンツを配置するようにしてみました。これもスライド全体がのっぺりと動くより、少しリッチな印象にできるのではないかなと思います。
デモのポイント
視差効果(パララックス)を付ける
スライド外に独自要素を置いて連動させる
視差効果(パララックス)を付ける
仕組みは簡単で、前のスライドと次のスライドの画像の位置を、アクティブなスライド側(中央)にずらしておくだけです。そうすると、スライドの移動距離よりも画像の移動距離が少なくなり、奥行きを感じられるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | .mv05 { /* 前後のスライド */ .swiper-slide { &[class*=-active] { .slide-media.clip img { transform: scale(1.2); } } &[class*=-prev] { .slide-media img { transform: translateX(80vw); } } &[class*=-next] { .slide-media img { transform: translateX(-80vw); } } } } |
また、clip-path を使ってガラスのフレーム風の装飾も入れてみました。
1 2 3 4 | <div class="slide"> <div class="slide-media clip img-cover"><img src="https://picsum.photos/id/139/2400/1500" alt=""></div> <div class="slide-media img-cover"><img src="https://picsum.photos/id/139/2400/1500" alt=""></div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | .slide-media { height: 800px; @media only screen and (max-width: 1024px) { height: max(70vh, 70vw); } img { transition: 3s; } &.clip { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; filter: contrast(1.1); --stroke-width: 1.2rem; --out: 2.4rem; --in: calc(var(--out) + var(--stroke-width)); clip-path: polygon( var(--out) var(--out), calc(100% - var(--out)) var(--out), calc(100% - var(--out)) calc(100% - var(--out)), var(--out) calc(100% - var(--out)), var(--out) var(--in), var(--in) var(--in), var(--in) calc(100% - var(--in)), calc(100% - var(--in)) calc(100% - var(--in)), calc(100% - var(--in)) var(--in), var(--out) var(--in) ); } } |
スライド外に独自要素を置いて連動させる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <div class="mv05 l-section"> <div class="swiper"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="slide"> <div class="slide-media clip img-cover"><img src="https://picsum.photos/id/139/2400/1500" alt=""></div> <div class="slide-media img-cover"><img src="https://picsum.photos/id/139/2400/1500" alt=""></div> </div> </div> ⋮ </div><!-- /swiper-wrapper --> <div class="content-wrapper"> <div class="content"> <h2 class="content-title"><span class="marker">Lorem ipsum</span></h2> <p class="content-text"><span class="marker">Dolor sit amet, consectetur adipiscing elit.</span></p> <p class="content-link"><span class="marker"><a href="#" class="content-button">VIEW MORE</a></span></p> </div> ⋮ </div> <div class="fraction"> <span class="current"><span class="num"></span></span> <span class="total"></span> </div> </div><!-- /swiper --> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | const myDelay = 7000; const slideLength = document.querySelectorAll('.mv05 .swiper-slide').length; const total = ('00' + slideLength).slice(-2); // スライド番号の要素取得 const fractionNum = document.querySelector('.mv05 .fraction .num'); const fractionTotal = document.querySelector('.mv05 .fraction .total'); fractionTotal.textContent = total; // スライド番号の切り替え const updateFraction = (index) => { let current = ('00' + (index + 1)).slice(-2); fractionNum.classList.add('anm-started'); setTimeout(() => { fractionNum.textContent = current; }, 400); } // スライド外コンテンツの切り替え // アニメーション開始 const startAnimation = (index) => { let activeSlide = document.querySelectorAll('.mv05 .content')[index]; activeSlide.classList.remove('anm-finished'); activeSlide.classList.add('anm-started'); } // アニメーション終了 const finishAnimation = () => { let activeSlide = document.querySelector('.mv05 .content.anm-started'); if (activeSlide) { activeSlide.classList.remove('anm-started'); activeSlide.classList.add('anm-finished'); } } const mySwiper = new Swiper('.mv05 .swiper', { ⋮ on: { slideChange: (swiper) => { updateFraction(swiper.realIndex); finishAnimation(); }, slideChangeTransitionStart: (swiper) => { startAnimation(swiper.realIndex); }, slideChangeTransitionEnd: () => { fractionNum.classList.remove('anm-started'); }, } }); |
同じページに同じスライダーを複数設置する方法
デモ内では紹介できなかったので、最後に複数の同じスライダーを設置する場合の書き方についても簡単にご説明しておきたいと思います。
1 2 3 4 5 6 7 8 9 | // NG例 const mySwiper = new Swiper('.hoge .swiper', { slidesPerView: 1, spaceBetween: 24, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, }); |
このように普通に初期化の記述を書けば、同じクラス名の要素はすべてスライダーとして動作するようになります。
しかし、この書き方だと「前へ / 次へ」ボタンやページネーションが正しく連動しません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // OK例 const slider = document.querySelectorAll('.hoge .swiper'); for (let i = 0; i < slider.length; i++) { let prev = slider[i].querySelector('.swiper-button-prev'); let next = slider[i].querySelector('.swiper-button-next'); const mySwiper = new Swiper(slider[i], { slidesPerView: 1, spaceBetween: 24, navigation: { nextEl: next, prevEl: prev, }, }); } |
このように、各スライダーごとに個別に要素を取得するように変更すれば、正常に動作するようになります!
まとめ
いかがでしたでしょうか。
本記事で触れていないオプションがまだまだあるのですが、ご紹介したものだけでもよく出てくるパターンは大体作れてしまうのではないかなと思います。
他にもどんなものが作れるのか、ぜひ公式サイトを覗いてみてください。
jQuery非依存でもっと軽量のプラグインは他にも出てきていますが、利用者数が多くノウハウが溜まっていることと、ずっと活発に開発され続けているという信頼感は大きいですよね。
バージョン5系から早々にSwiperのサポート対象外になってしまっていたIEも、今年の6月にサポート終了ということで、今後は最新バージョンを心おきなく使えるようになるのではないでしょうか。
今後のアップデートにも期待していきたいと思います。