WEBサイトにスライダーを実装したことがある人は多いですよね。
そんなときいつも何を使用していますか?
スライダーのJSライブラリで有名どころとして以下の3つがあります。
・slick
・Swiper
・keen-slider
私は今までslickを使っていました。
Swiperは機能がありすぎてよくわからないのと、slickよりも容量が重いからという理由もあります。
keen-sliderはカスタマイズ前提のライブラリで、アクセシビリティ(a11y)のサポートもされていないため使っていません。(slickとSwiperはアクセシビリティのサポートがされています。)
…
というような理由で、私はslick推しでした。
しかし、slickの操作感(いわゆるUX)があまり良くないと思っていました。
具体的には、マウスでドラッグしたときやスマホで指でスワイプしたときの動きが滑らかではないのです。
そんなとき、Splideというスライダーに出会いました。
Swiperのような滑らかな操作性があり、何といっても容量がめちゃくちゃ軽いのです。
しかも、調べてみるとslickでできることはだいたいSplideでもできるようです。そのため、slickではなくSplideを使ってみようと思いました。
ただ、本当に実務で使えるような柔軟性のあるライブラリなのかどうかを確かめないといけません。
そこで、こちらのSwiperで実装した記事のデモをSplideでも実装できるか検証してみたのがこの記事です。
結果として、Swiperの記事では15個デモがあるのですが、Swiperで実装している15個目のパララックスのデモ以外はSplideでも実装することができました。
Splideで作ったデモページを見る
Splideで実装してみて、Swiperの方が良いケースやそうじゃないケースなど色々わかったので、それについても解説していきたいと思います。
本記事では Splide v4.0.7(記事執筆時の最新)を使用しています。
目次
- 1 Splideってどんなプラグイン(ライブラリ)?
- 2 Splideの導入方法
- 3 HTML基本構造とクラス名
- 4 この記事で扱うデモの基本的なHTML構造
- 5 #01 基本 基本のスライダー
- 6 #02 基本 右側だけはみ出た形のスライダー
- 7 #03 基本 コンテンツ幅からはみ出たスライドを薄くする
- 8 #04 基本 ブレークポイント切り替えでSPだけスライダーにする
- 9 #05 応用 総スライド数が perPage を超える時だけスライダーにする
- 10 #06 基礎 ゆっくり動き続ける無限ループスライダー
- 11 #07 基本 サムネイル(スライダー)を付けて連動させる
- 12 #08 応用 サムネイル(非スライダー)を付けて連動
- 13 #09 基礎 スライダーの向きを交互に変える
- 14 #10 応用スライダーを入れ子にする
- 15 #11 基本メインビジュアル フェードアニメーション
- 16 #12 応用始めと終わりで別々のアニメーションを付ける
- 17 #13 応用 縦方向のスライダー
- 18 #14 応用 スライドごとに表示時間を変えて、進捗状況と連動したプログレスバーを作成する
- 19 まとめ
- 20 どうしても実装できなかったパララックスのデモ
Splideってどんなプラグイン(ライブラリ)?
公式サイト
以下、Splideの特徴です。
これ以外にももちろんありますがいくつかピックアアップしました。
特徴
- ・めちゃくちゃヒットしているらしい
- ・他のライブラリに依存していない
- ・ドキュメントが日本語
- ・容量がめちゃくちゃ軽い
- ・ドラッグ(スワイプ)したときの操作感が良い
- ・アクセシビリティ(a11y)にも配慮した作りになっている
- ・%やremなどの相対単位に対応
上記の特徴について簡単に説明します。
めちゃくちゃヒットしているらしい
Splideは現在、jsDelivr(CDN)において、月間3億ヒット以上するライブラリにまで成長しました✨
この数値はカルーセルライブラリとして有名なSwiperの3倍ですので、なかなか善戦しているのではないかと思います(もちろん、ライブラリの配信方法は様々ですので、及ばないところはまだまだあります)。Splideを選んでいただいた方々、そしてスポンサーの皆様、ありがとうございます!
引用元:バージョン4について – Splide
上記のとおり、かなり使われているようです。
ただ、この記事を書く前に他の方が書いている記事を参考にしようとしたのですがあまり無く、そのことがこの記事を書くモチベーションにもなりました(笑)。
他のライブラリに依存していない
slickであればjQueryに依存していますので必ずjQueryを読み込まないと動きません。
しかしSplideは他のライブラリに依存していないので、jQueryを読み込む必要がありません。
ドキュメントが日本語
Splideの開発者様が日本人であるためドキュメントが日本語です!
英語が苦手な日本人にとってかなりありがたいですね。
Swiperやslickなどは英語なので、何か調べようと思っても結局日本人が書いたブログ記事を読んでしまうんですよね(笑)
容量がめちゃくちゃ軽い
これはかなりいいポイントです!
jsファイルだけで比較すると、
slick(v1.8.0)はslick.min.jsファイルが42kBです。(jQueryに依存しているのでjQuery分を足すともっと増えます)
Swipe(v8.8.2)は、swiper-bundle.min.jsが142kBです
最後に、Splide(v4.0.7)は、splide.min.jsが29kBです。
この中で断トツで容量が軽いですね。最高です。
ドラッグ(スワイプ)したときの操作感が良い
Splideは操作感が良いです。Swiperと同じような心地よい操作感があります。
slickはあまり操作感が良くないのがデメリットだと感じていたのでこの点はかなりいいですね。
%やremなどの相対単位に対応
スライドの幅や高さは、Splideのオプションで設定でき、相対単位を使用することができます。
またこれは公式ドキュメントに書かれていませんが、CSSの比較関数(min,max,clamp)を指定することもできますのでかなり柔軟にスライドの幅と高さを制御することができます。
アクセシビリティ(a11y)にも配慮した作りになっている
slickやSwiperでも一応、アクセシビリティ(a11y)をサポートしています。
しかし、Splideのアクセシビリティ(a11y)への力の入れようは半端ないです。
アクセシビリティのことをなぜa11y(またはA11Y)と略すのか、それはAccessibilityのAからYまでの間に11文字が挟まれているためです。
Accessibility (アクセシビリティ)
そもそも、なぜスライダーにアクセシビリティ対応をしないといけないのでしょうか?
公式ドキュメントには以下のように書かれています。
カルーセルUIは、Webサイトのコンテンツを少ないスペースで魅力的に表現できる一方、致命的なアクセシビリティの問題を抱えています。例えば「スライドを変更して新しく表示された内容を読む」という基本的な動作でさえ、スクリーンリーダのみを通して行おうとすると、途端に容易なものではなくなります。
引用元:アクセシビリティ – Splide
このような課題から、次のように説明しています。
Splideは、すべてのユーザが快適にスライダーを使えるよう、アクセシビリティの向上に努めています。バージョン4からは、W3CのCarousel Design Patternに準拠したうえで、スクリーンリーダーが動的にコンテンツを読み上げられるようライブリージョンも導入しました。
引用元:アクセシビリティ – Splide
なんという力の入れかた!
特に、Splide最新バージョンのv4から導入されたライブリージョン。
これについては以下のように説明されています。
非表示のスライドが表示された際、新たに表示されたコンテンツをスクリーンリーダーを通して読み取るのは、決して容易なことではありません。我々はそれを視覚情報を頼りに認識できますが、聴覚あるいは点字による情報に頼っているユーザにとって、ボタンとは別の場所が動的に更新されてしまうスライダーは、とても扱いにくい存在です。
これを解決するための最大の武器が、ほかならぬライブリージョンなのですが、(略)
引用元:アクセシビリティ – Splide
どうやらかなり難しい技術だということがわかります。
このような難しい技術にも、「すべてのユーザが快適にスライダーを使えるようにしたい」という想いのもと積極的に取り組まれているのがSplideなのです。
アクセシビリティについて、より詳しくは公式ドキュメントを参照してください。
Splideの導入方法
Splide導入のためには3つの方法があります。
01 Splideのインストール
以下の3つの方法でインストールが可能です。
NPMでインストール
CDNを利用
ファイルをダウンロードして利用
この記事では3つ目の「ファイルをダウンロードして利用」のやり方で導入する手順についてご紹介します。
その他の方法については、公式サイト 基本的な使い方をご確認ください。
ファイルをダウンロードする場合
GitHubに移動し、ソースコード一式をダウンロードします。
手順は以下の通りです。
①「Code」をクリック。
②「Download ZIP」をクリック。
ダウンロードしたzipファイルを解凍すると以下のようなフォルダやファイルを確認することができます。
このうち、以下のファイルを使用します。
・dist/css/splide-core.min.css
・dist/js/splide.min.js
cssはいくつか種類がありますが、スライダーの見た目を完全にカスタマイズしたい場合はsplide-core.min.cssを使うといいでしょう。
スライダーの見た目を完全にカスタマイズしたい場合は、splide-core.min.cssを選ぶとよいでしょう。このファイルは必要最低限のスタイルしか含まれていないため、セレクタによる不要な上書き作業を避けることができます。そうでない場合は、splide.min.cssあるいはsplide-[theme].min.cssから選んでください。
引用元:基本的な使い方 – Splide
ダウンロードしたファイルを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/splide-core.min.css" /> </head> <body> CONTENTS <script src="path/splide.min.js"></script> </body> </html> |
02 HTMLにスライド要素を追加
1 2 3 4 5 6 7 8 9 10 11 | <section class="splide" aria-label="Splideの基本的なHTML"> <div class="splide__track"> <div class="splide__list"> <div class="splide__slide">Slide 01</div> <div class="splide__slide">Slide 02</div> <div class="splide__slide">Slide 03</div> </div> </div> </section> |
上記が基本の作りです。
公式サイトではsplide__listとsplide__slideがulとliタグですが、divタグでも問題ありません。
.splide .splide__track .splide__list .splide__slideの4つは必須です。
また、HTMLの構成には制限があります。
次の3点が守られていればどのような構成でも問題なく動きます。
※初見だとすぐ理解できないと思うので落ち着いて読んでみてください。
- ・splide__trackとsplide__listには任意の要素を追加することはできない。
- ・splide__listはsplide__trackの直下の子要素でなければいけない。
- ・splide__slideはsplide__listの直下の子要素でなければいけない。
そのため、以下のような構成は正しく動作しません。
1 2 3 4 5 6 7 8 9 10 11 | <section class="splide" aria-label="無効なHTMLの例"> <div class="splide__track"> <div> <div class="splide__list"> <div class="splide__slide">...</div> </div> </div> </div> </section> |
違反しているルール
- ・splide__trackとsplide__listには任意の要素を追加することはできない。
以下の構造は正しく動作します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <section class="splide" aria-label="HTMLの制限の例"> <div> <div> <div> <div class="splide__track"> <div class="splide__list"> <div class="splide__slide">...</div> </div> </div> </div> </div> </div> </section> |
このほかに、aria-labelなどの簡単なラベルをつける必要があります。
Splideがアクセシビリティを強く意識している点がうかがえますね。
これについては公式ドキュメントに詳しく記載がありますのでそちらを参考にしてください。
基本的な使い方
03 JSでスライダーの初期化を行う
1 2 3 | new Splide( '.splide' ).mount(); |
このように書くことでスライダーが動き出します。
.mount()を呼ばないと全く動かないし、何も表示されないので注意してください。
次のように、Splideクラスをインスタンス化してからmountメソッドを呼んで初期化することも可能です。
このように書くことで初期化の前に色々な処理を挟んだり、処理を登録することができるので、基本的にはこの書き方が良いと思います。
1 2 3 4 | const mySplide = new Splide( '.splide' ); mySplide.mount(); |
もし、ページ内に複数のスライダーがある場合は1つずつインスタンスを作成する必要があります。
1 2 3 4 5 6 7 | new Splide( '#slider1' ).mount(); new Splide( '#slider2' ).mount(); new Splide( '#slider3' ).mount(); |
あるいは、以下のようにまとめて初期化することもできます。
1 2 3 4 5 6 7 | var elms = document.getElementsByClassName( 'splide' ); for ( var i = 0; i < elms.length; i++ ) { new Splide( elms[ i ] ).mount(); } |
04 CSSでデザイン調整を行う
最後に、CSSでデザイン調整をします。
本記事では、splide-core.min.cssを読み込みましたが、これだと必要最低限のスタイルしか当たらないので、ご自身でスタイル調整する必要があります。
この後紹介する「HTML基本構造とクラス名」なども参考に、検証ツールを開いて、クラスにスタイルをあてていきましょう!
HTML基本構造とクラス名
HTML構造については公式ドキュメント紹介されています。
ただ、図解としての説明はないので、この記事では視覚的にわかりやすいように解説します。
この図解で記載している各クラス名はオプションで指定すれば任意のクラス名に変えることもできますが、本記事では全てデフォルトのままという前提で説明しています。
公式ドキュメントと照らし合わせながら読むとより理解が深まるでしょう。
.splide | スライダー全体を囲むコンテナです。 |
.splide__track | このエリア内にスライド要素が収まります。 はみ出させたい場合は overflow: visible; で上書きします。 |
.splide__list | .splide__list(各スライド)を囲む要素です。 これを transform で移動させることでスライダーの動きを実現しています。 .splide__list の直下には .splide__slide だけを入れます。 |
.splide__slide | この中に各スライドの要素を入れます。 デザインを調整する際、.splide__slide に直接スタイルを当ててもいいですが、 以下のように中身を囲う要素を作って、それに対してスタイルを当てるのがお勧めです。 <div class="splide__slide"> <div class="item"> <img src="img.jpg" alt=""> <p class="text">テキストが入ります</p> </div> </div> |
.splide__pagination | ページネーション 出力されるHTMLについての詳細はこちら: ページネーションの調整 |
.splide__arrow–prev | .splide__arrow–prev | 矢印 それぞれのボタンには共通のクラス名として、.splide__arrowが付与されます。 また、前のスライド / 次のスライドがないときには、disabled属性が付与されます。 出力されるHTMLについての詳細はこちら:矢印の調整 |
.splide__track > .splide__list > .splide__slideの構造は守る必要がありますが、ページネーションや矢印ボタンは、.splide__trackの外に出しても動作します。
※.splideの外には出せません。
ここで、.splide__slideに付与されるクラス名をもう少し詳しく見てみましょう。
.is-prev | 前のスライドにつくクラス名。 |
.is-next | 次のスライドにつくクラス名。 |
.is-active | アクティブ状態のスライドにつくクラス名。 |
.is-visible | .splide__trackに含まれるスライドにつくクラス名。 |
これらのクラス名は、スライドの状況によって動的に変わるクラス名で、.splide__slideに対して付与されます。
例えば、「アクティブなスライドは明るくして、そうではないスライドは暗くする」とった場合、
アクティブなスライドにはis-activeが付与されるので、これを利用してCSSで調整などを行います。
この記事で扱うデモの基本的なHTML構造
1 2 3 4 5 6 7 8 9 10 11 12 13 | <div class="splide" aria-label="Splideの基本的なHTML"> <div class="splide-wrapper"> <div class="splide__track"> <div class="splide__list"> <div class="splide__slide">Slide 01</div> <div class="splide__slide">Slide 02</div> <div class="splide__slide">Slide 03</div> </div> </div> </div> </div> |
この記事のデモのHTML構造では、.splide__trackの親要素として.splide-wrapperを挟んでいます。
そして、.splide-wrapperに対してposition:relative;を共通のスタイルとして付与しています。
理由は、矢印を.splide__trackに対して上下中央に配置するためです。
この点を心に留めて読んでいただければと思います。
もし矢印をトラックを基準として上下中央に揃えたい場合、relativeポジションを持った任意の要素の中にプレースホルダを配置することで実現できます。
これは、スライダーがトグルボタンなどほかのコントロールを持っている場合に効果を発揮します。
引用元:矢印の調整 – Splide
#01 基本 基本のスライダー
それではここから各デモの解説に入っていきます。
まずはよく見る以下のようなスライダーです。
このデモですが、Swiperのデモと違う点があります。
それは、PCサイズ(1025px以上)のときスライダーが1枚ずつ移動するのではなく、4枚ずつ移動する点です。
※正確には4枚ずつ移動したあと、2枚移動する。
なぜこのようにしているかについて後ほど詳しく解説しますが、
結論、Swiperのデモのように1枚ずつ移動させたいのであれば、Swiperで実装したほうがいいです。
デモのポイント
矢印をスライドに対して上下中央揃えにする
矢印をカスタマイズする
ホバー効果でスライドを動かしたり、影を付けたりする場合の調整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | const target = '.card01 .splide'; const options = { mediaQuery: 'min', perPage: 1, gap: 24, breakpoints: { 600: { perPage: 2, }, 1025: { perPage: 4, gap: 32, } }, } const mySplide = new Splide(target, options); mySplide.mount(); |
■使用しているオプション
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
mediaQuery | string | ‘max’ | ブレークポイントの検出に用いられるメディアクエリに対して、min-width、max-widthのどちらを使うかを指定します。 ‘min’:min-widthを使用する。 |
perPage | number | 1 | 1ページに何枚のスライドを表示するかを指定します。正の整数で指定し、小数の値は指定しないでください。 |
gap | number | string | スライド間の余白を指定します。1emなどの相対単位を指定することも可能です。 | |
breakpoints | object | ブレークポイントごとにオプションを変更する。 < 例 >640px以下でスライドの数を4枚から2枚にする { perPage: 4, breakpoints: { 640: { perPage: 2, }, } } < 例 >640px以上でスライドの数を2枚から4枚にする { mediaQuery: ‘min’, perPage: 4, breakpoints: { 640: { perPage: 2, }, } } |
■使用しているメソッド
メソッド | 説明 |
---|---|
mount | インスタンスを初期化します。拡張のためのエクステンションや、トランジションコンポーネントを渡すことができます。 |
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 | <div class="card01 l-section"> <div class="l-inner"> <div class="splide" aria-label="#01基本 カード型01"> <div class="splide-wrapper"> <div class="splide__track"> <div class="splide__list"> <a href="#" class="splide__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="splide__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> </div> </div> </div> <ul class="splide__pagination"></ul> </div> </div> </div> |
矢印をスライドに対して上下中央揃えにする
ページネーションがあると、ページネーションを含んだ高さに対して矢印が上下中央配置されてしまいす。
そのためページネーションの位置を.splide__trackの外から出します。
こうすることで、スライドの高さに対して上下中央配置されます。
矢印をカスタマイズする
矢印(.splide__arrows)は.splide__trackの直近の親要素に配置されます。
このデモサイトの場合は.splide__trackの直近の親要素は.splide-wrapperなので、以下の位置に配置されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <div class="splide"> <div class="splide-wrapper"> <!-- ★矢印のHTMLは、.splide__trackの直近の親要素に配置される --> <div class="splide__arrows splide__arrows--ltr"> ... </div> <div class="splide__track"> ... </div> <ul class="splide__pagination"></ul> </div> </div> |
.splide__arrowsの中身は次のようなHTMLが生成されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <div class="splide__arrows splide__arrows--ltr"> <button class="splide__arrow splide__arrow--prev" type="button" aria-label="Previous slide" aria-controls="splide01-track" > <svg>...</svg> </button> <button class="splide__arrow splide__arrow--next" type="button" aria-label="Next slide" aria-controls="splide01-track" > <svg>...</svg> </button> </div> |
デフォルトでは、矢印のアイコンがsvgで表現されます。
これを別のパスに変えたい場合は、オプションのarrowPathに、別のSVGコードを設定します。
ただ、SVGではなく別の画像ファイルに変更したい場合もありますよね。
そのような場合は、以下のように、arrowPathに空文字にすれば消せると思いきや…消せません。
1 2 3 4 5 6 | //このようにしても消せない! new Splide( '.splide', { arrowPath: '', } ).mount(); |
解決方法としては、①CSSで消す。②HTMLに矢印用のHTMLを直接書く。という方法があげられます。
①CSSで消す場合
1 2 3 4 5 | .splide__arrow > svg { display: none; } |
このように強制的に消し、.splide__arrowに対して疑似要素などで画像を配置すればOKです。
②HTMLに矢印用のHTMLを直接書く
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <div class="splide"> <div class="splide-wrapper"> <div class="splide__arrows"> <button class="splide__arrow splide__arrow--prev"></button> <button class="splide__arrow splide__arrow--next"></button> </div> <div class="splide__track"> ... </div> <ul class="splide__pagination"></ul> </div> </div> |
このように、直接書けばsvgタグは生成されないので、cssで消す必要はありません。
あとは.splide__arrowに対して疑似要素などで画像を配置すればOKです。
ここは正直、オプションでsvgを消せるようにしてほしいですね。。
また、.splide__arrowsを、.splide__trackよりも後に置くこともできますが、これはしないようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <div class="splide"> <div class="splide-wrapper"> <div class="splide__track"> ... </div> <!-- 【これはやめよう】.splide__trackのあとに矢印を置かない!! --> <div class="splide__arrows"> <button class="splide__arrow splide__arrow--prev"></button> <button class="splide__arrow splide__arrow--next"></button> </div> <ul class="splide__pagination"></ul> </div> </div> |
このようにすると、画像の上に矢印があるスライダーの場合、矢印が一瞬スライダーの下にもぐるような事象がおきます。
■参考
矢印の調整
ページネーションの調整
ホバー効果でスライドを動かしたり、影を付けたりする場合の調整
このデモでは、スライダーにホバーしたとき上に16px動かす効果を付けています。
ただ、「ホバーしたらスライドの位置を16px上へ」というCSSだけ書いても上手くいきません。
理由は、.splide__trackに.overflow: hidden;がかかっているからです。
それを解決するために、.splide__trackに、はみ出す分だけpaddingを付けておくようにします。
このデモでは16px上にはみ出させたいので、.splide__trackに、padding-top: 16px;を付けています。
なぜ4枚ずつ移動させているのか?
4枚ずつと言っていますが、最初に矢印を押したときは4枚移動し、次に矢印を押したときは2枚だけ移動して最後のスライドまで表示される形です。
これは特にこちらで何かオリジナルのスクリプトを書いているというわけではなく、Splide側でそのようにしています。
さて、なぜこのデモは4枚ずつ移動するようにしているのかについて説明します。
まず、このデモを作成するにあたり最初、オプションを以下のように設定していました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const options = { mediaQuery: 'min', perPage: 1, perMove: 1,//★追加 gap: 24, breakpoints: { 600: { perPage: 2, }, 1025: { perPage: 4, gap: 32, } }, } |
Swiperのデモでは、1025px以上のときにslidesPerViewを4にしていたので、
Splideでは1025pxのときperPageを4にしています。
ただ、それだけだと1枚ずつ移動しません。そのため、perMoveというオプションを設定し、値を1とすることで1枚ずつ移動するようにしました。
上記のオプションをもとに作成されたデモが以下になります。
ソースを見る
1枚ずつ動くのでイイ感じに見えますが、ページネーションの数が3つのままです。
また、ページネーションをクリックしたときの挙動が、矢印を押したときの挙動と異なるため違和感があります。
そこで、ページネーションの数をスライドの数分出すように調整したのが以下のオプションです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const options = { mediaQuery: 'min', perPage: 1, perMove: 1, gap: 24, focus: 'center',//★追加 breakpoints: { 600: { perPage: 2, }, 1025: { perPage: 4, gap: 32, } }, } |
focusというオプションを新たに追加し、値をcenterとするとページネーションの数がスライドの数分になります。
このオプションをもとに作成したデモが以下になります。
ソースを見る
見ていただくとわかるように、矢印を押してもスライドが移動せずページネーションのアクティブだけ変わる時があり、動きが微妙です。
もちろん、アクティブなスライドのときはそのスライドを明るくするなどの対策があるかもしれませんが、デザイン上それができないときもあるので不採用としました。
このような経緯から、perMoveや、focusは指定せず、perPageのみ指定するようにしました。
Swiperとslickは、矢印を押したときにスライドが動かない分についてはページネーションを表示せずイイ感じの動きになりました。
そのため、「見えるスライドの数が4枚で、1枚ずつ移動するスライダーで、ページネーションを押したときもイイ感じに動かしたい」という場合は、Swiperで実装したほうが楽です。
※slickでもいいですがやはり操作感に関してSwiperが勝るため。
#02 基本 右側だけはみ出た形のスライダー
次は、初期状態で右側だけはみ出たスライダーです。
デモのポイント
初期状態で右側だけコンテンツ幅からはみ出る
スライドの横幅を固定値(px)にする
矢印とページネーションを横並びにする
どのスライドが選択されているのかわかるようにする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const target = '.card02 .splide'; const options = { mediaQuery: 'min', perMove: 1, //1度に何枚動かすか fixedWidth: '240px', gap: 16, updateOnMove: true, breakpoints: { 1025: { fixedWidth: '360px', gap: 32, } }, } const mySplide = new Splide(target, options); mySplide.mount(); |
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
mediaQuery | string | ‘max’ | ブレークポイントの検出に用いられるメディアクエリに対して、min-width、max-widthのどちらを使うかを指定します。 ‘min’:min-widthを使用する。 |
perMove | number | 1度の移動で、何枚のスライドを移動するかを指定します。正の整数で指定し、小数の値は指定しないでください。 | |
fixedWidth | number | string | 各スライドの幅を固定します。CSSの相対単位を指定することも可能です。この値を設定した場合、perPageオプションは無視されます。 | |
gap | number | string | スライド間の余白を指定します。1emなどの相対単位を指定することも可能です。 | |
updateOnMove | boolean | false | is-activeクラスをスライドの移動前に更新するかどうかを決定します。 |
breakpoints | object | ブレークポイントごとにオプションを変更する。 < 例 >640px以下でスライドの数を4枚から2枚にする { perPage: 4, breakpoints: { 640: { perPage: 2, }, } } < 例 >640px以上でスライドの数を2枚から4枚にする { mediaQuery: ‘min’, perPage: 4, breakpoints: { 640: { perPage: 2, }, } } |
■使用しているメソッド
メソッド | 説明 |
---|---|
mount | インスタンスを初期化します。拡張のためのエクステンションや、トランジションコンポーネントを渡すことができます。 |
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 | <div class="card02 l-section"> <div class="l-inner"> <div class="splide" aria-label="#02基本 カード型02"> <div class="splide-wrapper"> <div class="splide__track"> <div class="splide__list"> <a href="#" class="splide__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="splide__slide"> <article class="slide"> <div class="slide-media img-cover"><img src="https://picsum.photos/id/1060/800/500" alt=""></div> <div class="slide-content"> <time class="slide-date" datetime="2021-12-01">2021.12.01</time> <h2 class="slide-title">Sed eiusmod tempor incidunt ut labore et dolore magna aliqua.</h2> </div> </article> </a> </div> </div><!-- /splide__track --> </div> <div class="splide-controller"> <ul class="splide__pagination"></ul> <div class="splide__arrows"></div> </div> </div><!-- /splide --> </div> </div> |
初期状態で右側だけコンテンツ幅からはみ出る
これは以下のようにCSSで調整すれば簡単にできます。
1 2 3 4 5 6 | .card02 { overflow: hidden; // 2.代わりに親要素で hidden にする .splide__track { overflow: visible; // 1.はみ出させるように visible で上書き } } |
頑張ってmargin-leftなどを使ってやろうとすると上手くいかないので注意しましょう。
スライドの横幅を固定値(px)にする
perPageでスライド数を指定すると、コンテナのサイズに応じてスライドのサイズも動的に変わる形になります。
そうではなく、「どの画面幅でも常に〇pxにしたい」という場合は、fixedWidthを使用します。
▼重要な部分だけ抜粋
1 2 3 4 5 | const options = { fixedWidth: '240px', } |
fixedWidthには相対値も指定することができます。
また、ここで指定した値は、.splide__slideにインラインスタイルでstyle=”width: 360px;”という形で付与されます。
そのため、比較関数を指定して書くこともできます。
1 2 3 4 5 | const options = { fixedWidth: 'min(360px,20vw)', } |
このように、fixedWidthを使えばかなり柔軟にスライドの幅を指定することができるので、スライドの幅を変えたい場合は基本的にJS側で制御するのが楽でおすすめです。
もしCSSで各スライドの横幅を指定したい場合は、autoWidthをtrueにするとCSSで変更できるようになります。
ただし、autoWidthをtrueにすると、fixedWidthは機能しなくなるので注意してください。
fixedWidthを使いたいのであれば、autoWidthをfalseにするか、そもそもオプションに記述しないようにしましょう。
▼参照
autoWidth
矢印とページネーションを横並びにする
このデモでは、.splide__paginationと、.splide__arrowを、.splide-controllerで囲み、それに対してdisplay:flexを適用して横並べにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <div class="card02 l-section"> <div class="l-inner"> <div class="splide" aria-label="#02基本 カード型02"> <div class="splide-wrapper"> ⋮ </div> <div class="splide-controller"> <ul class="splide__pagination"></ul> <div class="splide__arrows"></div> </div> </div> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | .card02 { .splide-controller { display: flex; gap: 1.6rem; align-items: center; margin-top: 3.2rem; } .swiper-pagination { display: flex; flex-wrap: wrap; gap: 1.2rem 0.8rem; margin: 0 auto 0 0; text-align: center; > li { font-size: 0;/* liタグの下にある余白を消すため */ } } } |
どのスライドが選択されているのかわかるようにする
デモではアクティブ状態ではないスライドは少し暗くして、現在アクティブ状態のスライドは明るくすることでわかりやすくしています。
これはCSSで対応可能です。
1 2 3 4 5 6 7 8 9 10 11 | .card02 { .splide__slide { opacity: 0.6; transition: opacity .3s ease; &.is-active { opacity: 1; } } } } |
スライドがアクティブ状態になると、is-activeクラスが.splide__slideに付与されるのでそれを利用します。
【補足】矢印を押したときのページネーションとスライドの挙動について
ここで、Swiperで作成したデモと見比べてみましょう。
右矢印を7回押しときのページネーションの動き方とスライドの動き方を比べてみてください。
Swiperの方は、7回矢印を押すだけで最後のスライドまで到達し、Splideの方はスライドの総数分の10回押さないとページネーションが最後まで到達しません。
これはSwiperがとても優秀で、「もう最後のスライドまで見えてるし10回押さなくてもいいよね!」ということでページネーションの数と、矢印を押す回数を減らしてくれています。
Splideはその点、真面目に10個分押す必要があるわけです。
でも別に気にならないと思いましたか?
それはSplideのデモでは分かりやすいように、アクティブスライドを明るくしているからです。
これを、Swiperのデモのように、アクティブスライドは明るくしないと以下のようになります。
ご覧の通り、7回目以降に右矢印を押しても、ページネーションのアクティブは変わりますが、それに対応するアクティブなスライドが全く分かりません。
このように、「全部のスライドの明るさは同じ」というデザインの場合はどうすればいいでしょうか?
このようなときは、Swiperで実装することをお勧めします。
理由は、Swiperのデモを見ての通り、そちらの方がわかりやすいからです。
#03 基本 コンテンツ幅からはみ出たスライドを薄くする
これもよく見るスライダーですね!
結論から言うと、これはSwiperで作った方がいいです。
Swiperで作った場合のデモは以下です。
Swiperで作ったデモを見る
Swiperで作ったデモとSplideで作ったデモを見比べると、スライドが明るくなったり暗くなる動きが、Swiperの方が違和感なくスムーズです。
Splideで作成した場合は、スライドが動いたあと少し遅延して明るくなったり暗くなります。
これは、is-visibleクラスの付け外しが、スライドが動いた後に付け外しされるためです。
Swiperの場合は、.swiper-slide-visibleクラスが、スライドが動くのと同時に付け外しされるので滑らかに見えるというわけです。
Splideでは、.is-activeについては、スライドが動く直前に付け外しできるようなオプションがありますが、.is-visibleに関しては、そのようなオプションが無いので今のところこれが限界という感じです。
これについてはSplideにupdateVisibleOnMoveみたいなオプションが追加されることを期待します。
デモのポイント
自動再生させる
無限ループさせる
コンテンツ幅からはみ出たスライドを薄くする
自動再生させる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | const target = '.card03 .splide'; const options = { ⋮ autoplay: true, interval: 3000, speed: 1000, pauseOnFocus: false, ⋮ } const mySplide = new Splide(target, options); mySplide.mount(); |
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
autoplay | boolean | false | 自動再生を有効にするかどうかを決定します。 |
interval | number | 5000 | 自動再生の間隔をミリ秒単位で指定します。 |
speed | number | 400 | スライダーの移動時間をミリ秒単位で指定します。 0を指定すると、アニメーションなしで直接対象に遷移します。 |
pauseOnFocus | boolean | true | スライダー内にフォーカスされたエレメントがある場合、自動再生を停止するかどうかを決定します。 |
このデモでは、pauseOnFocusをfalseにしています。
このようにすることで、ユーザーがドラッグなどの操作をしても自動再生が止まらないようになります。
ただ、公式ドキュメントではアクセシビリティの観点から、pauseOnFocusはtrueにしておくと良いとされています。
無限ループさせる
1 2 3 4 5 6 7 8 9 10 11 12 | const target = '.card03 .splide'; const options = { ⋮ type: 'loop', ⋮ } const mySplide = new Splide(target, options); mySplide.mount(); |
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
type | string | ‘slide’ | スライダーの種類を指定します。 slide: 一般的なスライドアニメーションによるスライダー |
コンテンツ幅からはみ出たスライドを薄くする
.splide__trackの中にあるスライド(.splide__slide)に、.is-visibleが付与されるのでこのクラスを利用します。
CSSで以下のように書けばOKです。
1 2 3 4 5 6 7 8 9 10 | .card03 { .splide__slide { &:not(.is-visible) { .slide { pointer-events: none; opacity: .3; } } } } |
ただ、先ほどもお伝えした通り、スライドが動ききった後に.is-visibleの付け外しが行われるので、少し遅延してスライドが明るくなったり暗くなります。
#04 基本 ブレークポイント切り替えでSPだけスライダーにする
デモのポイント
スライダーの解除↔再初期化を行う
スライダーが初期化されていないときのスタイル調整を行う
こちらはSwiperよりもとてつもなく簡単に実装できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const target = '.card04 .splide-a'; const options = { ⋮ breakpoints: { ⋮ 1025: { destroy: true // 破棄するが、引き続きブレークポイントを監視する } }, } const mySplide = new Splide(target, options); mySplide.mount(); |
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
destroy | boolean | スライダーを破棄するかどうかを決定します。 true: 破棄するが、引き続きブレークポイントを監視する |
destroyをtrueに設定すると、完全に破棄されず、ブレイクポイントを引き続き監視して、ブレイクポイント通過時だけ破棄するということが可能になります。
これでスライダーの解除と再初期化は実現可能です。とても簡単ですね。
スライダーが初期化されていないときのスタイル調整を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .card04 { /* スライダー非活性時 */ .splide-a:not(.is-active) { padding: 0; .swiper-button-prev, .swiper-button-next { display: none; } .splide__list { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; } } } |
スライダーが初期化されたとき、div.splideに、.is-activeクラスが付与されるのでこれを利用します。
おなじくdiv.splideに.is-initializedというクラスが付与されますがこれは使いません。
なぜなら、destroyしたときもdiv.splideに付与され続けるため、スライダーが活性状態なのか非活性状態なのかが分からないからです。
#05 応用 総スライド数が perPage を超える時だけスライダーにする
このデモが先ほどのデモと違う点は、「ブレイクポイント視点」ではなく、「スライドの枚数視点」というところです。
スライドの総スライド数が、perPageよりよりも多い時、つまり「総スライド数 > perPage」のときだけスライダーにするというデモです。
方針としては、予めブレークポイントと perPage のセットを配列に格納しておき、総スライド数と比較してスライダーの解除↔初期化を切り替えるという形をとっています。
ブレークポイントがさらに多い場合も、Splideのオプション(breakpoints)と配列にそれぞれ追加すれば対応できるので柔軟性があります。
デモのポイント
ブレークポイントと perPage のセットを配列に格納
スライダーの解除↔再初期化を行う
スライダーが初期化されていないときのスタイル調整を行う
ブレークポイントと perPage のセットを配列に格納
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // ブレークポイントごとの perPage を配列に格納 const breakpoints = [{ breakpoint: 0, perPage: 1, }, { breakpoint: 600, perPage: 2, }, { breakpoint: 1025, perPage: 4, } ]; breakpoints.reverse();// 配列を逆順にする |
まずブレークポイントと perPage のセットを配列に格納しておきます。
breakpoints.reverse() をしているのは、Splideのオプション(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 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 73 74 75 76 77 78 79 80 | let mySplide, myperPage; let isInited = false; const slideLength = document.querySelectorAll('.card04 .splide-b .splide__slide').length; const breakpoints = [{ breakpoint: 0, perPage: 1, }, { breakpoint: 600, perPage: 2, }, { breakpoint: 1025, perPage: 4, } ]; breakpoints.reverse(); const checkBreakpoint = () => { for (let key in breakpoints) { let bp = breakpoints[key]['breakpoint']; if (window.matchMedia('(min-width:' + bp + 'px)').matches) { myperPage = breakpoints[key]['perPage']; break; } } if (slideLength > myperPage) { if (!isInited) { initSplide(); isInited = true; } } else { if (isInited) { mySplide.destroy(); mySplide.refresh();//ポイント!! これをしないとbreakpointsでの再マウントがされなくなるため isInited = false; } } }; const initSplide = () => { const target = '.card04 .splide-b'; const options = { mediaQuery: 'min', perPage: 1, perMove: 1, //1度に何枚動かすか gap: 16, type: 'loop', pagination: false, speed: 1000, slideFocus: true, updateOnMove: true, rewind: false, breakpoints: { 600: { perPage: 2, }, 1025: { perPage: 4, gap: 32, } }, } mySplide = new Splide(target, options).mount(); } window.addEventListener('resize', function () { checkBreakpoint(); }); checkBreakpoint(); |
インスタンスメソッド | 引数 | デフォルト値 | 説明 |
---|---|---|---|
destroy | boolean | true | trueの場合、breakpointsでの再マントも行われなくなり、完全に破棄される。 |
上記の41行目が特にポイントです。
40行目でdestroy()を実行していますが、これだけだとブレイクポイントを監視してくれません。
41行目のrefresh()をさらに呼び出すことで、一度破棄されたあとも引き続きブレイクポイントを監視し、再マウントされるようになります。
■参考
refresh
ちなみに、なぜ#04応用のようにbreakpointsプロパティ内でdestroyを指定しないかというと、この実装は、「breakpointsの違いによってスライダーを破棄するかしないか」ではなく、「スライドの枚数がperPageより多いか少ないか」を基準に考えているためです。
そのため、breakpointsプロパティのオプションでdestroyするのではなく、Splideインスタンスのメソッドとして実行する必要があります。
つまり、オプションのdestroyを使うのではなく、インスタンスメソッドのdestroyおよびrefreshを使う必要があります。
インスタンスメソッドはAPIsというドキュメントにまとまっているので詳しくは以下を参考にしてください。
APIs
スライダーが初期化されていないときのスタイル調整を行う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | .card04 { /* スライダー非活性時 */ .splide-b:not(.is-active) { padding: 0; visibility: visible; .splide__arrow--prev, .splide__arrow--next { display: none; } .splide__list { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; } } } |
スライダーがアクティブなときはis-activeが付くので、それを利用してスタイル調整を行います。
#06 基礎 ゆっくり動き続ける無限ループスライダー
スライドの動きがずーっと止まらずに一定の速度で動き続けるタイプのデモです。
Splideの場合、その動きを「自動スクロール」と呼んでおり、Auto Scrollエクステンションという拡張機能を別途読み込む必要があります。
デモのポイント
ゆっくり動き続けるようにする
ホバーしたら他のスライドを薄くする
ゆっくり動き続けるようにする(準備編)
先ほどもお伝えしたとおり、Splideにはずーっと動き続ける自動スクロールを実装するためには拡張機能を使用します。
まずはそのための準備をしましょう。
ファイルをダウンロードする
それでは早速、拡張機能を使用するための準備をしていきましょう。
いくつか読み込む方法がありますが、今回はjsファイルをダウンロードして読み込む手順について見ていきましょう。
まずはGitHubに移動します。
手順は以下の通りです。
①「Code」をクリック。
②「Download ZIP」をクリック。
ダウンロードしたzipファイルを解凍すると以下のようなフォルダやファイルを確認することができます。
このうち、以下のファイルを使用します。
・dist/js/splide-extension-auto-scroll.min.js
ダウンロードしたファイルをHTMLで読み込む
1 2 3 4 5 6 7 8 | <script src="path/splide.min.js"></script> <!-- 以下を追記 --> <script src="path/splide-extension-auto-scroll.min.js"></script> </body> </html> |
これで準備完了です。
ゆっくり動き続けるようにする(実行編)
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 | const target = '.card05 .splide'; const options = { mediaQuery: 'min', fixedWidth: '24rem', gap: 16, type: 'loop', arrows: false, drag: 'free', flickPower: 300, pagination: false, autoScroll: { speed: 0.5, pauseOnHover: false, pauseOnFocus: false, }, breakpoints: { 1025: { gap: 32, fixedWidth: '36rem', } }, } const mySplide = new Splide(target, options); mySplide.mount(window.splide.Extensions);//拡張機能をセットする |
このデモでは上記のスクリプトを書いています。
自動スクロールで重要なポイントは以下の部分です。
1 2 3 4 5 6 7 8 9 10 11 12 | const options = { ... autoScroll: { ... } } ... mySplide.mount(window.splide.Extensions);//拡張機能をセットする |
まず、mountする際に、引数にwindow.splide.Extensionsを渡すことで自動スクロールが有効化されます。
自動スクロールが有効化されると、autoScrollオプションを使用することができます。
このデモでは以下のようにして使用しています。
1 2 3 4 5 6 7 8 9 10 | const options = { ... autoScroll: { speed: 0.5, pauseOnHover: false,//ホバーしても自動スクロールを止めない pauseOnFocus: false,//ドラッグしても自動スクロールを止めない } } |
autoScrollを通して変更できるオプションは以下です。
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
speed | number | 1 | スクロールの速度を「アニメーションフレーム当たりのピクセル」で決定します。 自動スクロールのスピードを遅くしたい場合は小さい数字を指定します。 |
autoStart | boolean | true | 初期化後、自動的にスクロールを開始するかどうかを決定します。 |
pauseOnHover | boolean | true | スライダーの上にマウスカーソルが乗ったとき、スクロールを停止するかどうかを決定します。 |
pauseOnFocus | boolean | true | スライダー内にフォーカスされた要素がある場合、スクロールを停止するかどうかを決定します。 |
ホバーしたら他のスライドを薄くする
1 2 3 4 5 6 7 8 9 10 | .card05 { /* スライド ホバー時 */ .splide:hover { .slide:not(:hover) { @media only screen and (min-width: 1025px) { opacity: .3; } } } } |
CSSの疑似クラス:notを使い、ホバーしたときにホバーしていないスライドはopacityを下げています。
このようにすることで、1つのスライドだけを目立たせ、注目させることができますね。
#07 基本 サムネイル(スライダー)を付けて連動させる
結論から言うと、これはSwiperで作った方がいいです。
Swiperで作った場合のデモは以下です。
Swiperで作ったデモを見る
大きな違いは、サムネイル下にあるスクロールバーです。
Swiperの場合は、scrollbarというオプションがあるためそれを利用していますが、Splideにはそれに相当するオプションがありません。
そのため自分でスクリプトを書く必要があります。
また、Swiperではそのスクロールバーをドラッグして移動させることができますが、Splideの場合はできません。(もちろん自分で頑張ってスクリプトを書けばできるかもですが…)
なので、もしSwiperのようなデモを作りたいのであれば、Swiperで実装したほうが圧倒的に楽です。
スクロールバーとしてではなく、単純に現在地表示用のバーを実装したいのであればSplideで可能です。
このデモでは、ページネーションのスタイルを変更してそれっぽくなるように表現していますが、Swiperのようにマウスで掴んで移動ができません。
ただこれだと、このデモと相性があまりよくありません。
理由は、バーの端がずっと左にあるので、サムネイルの2枚目以降をクリックしたときに、「これ以上左側にスライドがないのかな?」とユーザーを混乱させてしまう恐れがあります。
もちろん、メインスライダーに矢印があるのでわかるかもしれませんが、サムネイルスライダーだけを見ているユーザーもいるため、なるべくSwiperのデモのようにしたほうがユーザーに優しいUIでしょう。
デモのポイント
2つのスライダーを連動させる
スライダーの現在地表示
スライドが切り替わったらサムネイル画像を明るくする
2つのスライダーを連動させる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | const thumb = '.gallery01 .splide-thumb'; const thumb_options = { ⋮ } const thumbSplide = new Splide(thumb, thumb_options); ⋮ const main = '.gallery01 .splide-main'; const main_options = { ⋮ } const mainSplide = new Splide(main, main_options); mainSplide.sync(thumbSplide); mainSplide.mount(); thumbSplide.mount(); |
まず、サムネイル画像用のスライダーとメイン画像用スライダーのSplideインスタンスをそれぞれ作成します。(thumbSplideと、mainSplide)
次に、mainSplideにsyncメソッドを使用して、メソッドの引数にthumbSplideを渡し実行します。
最後に、両方のインスタンスをmountします。
これで2つのスライダーが同期します。
注意点としては、syncを実行するタイミングは、mountよりも前ということです。
■参考
サムネイルスライダー
スライダーの現在地表示
似たようなものにこちらのスライダーの現在地表示があります。
しかし、前述したようにこれだとこのデモとは相性が悪いので、ページネーションのスタイルを調整してそれっぽく表現しています。
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 | .gallery01 .splide__pagination { display: flex; flex-wrap: nowrap; justify-content: center; margin-top: 3.2rem; text-align: center; } .gallery01 .splide__pagination>li { width: 100%; } .gallery01 .splide__pagination__page { width: 100%; height: 4px; cursor: pointer; vertical-align: top; background-color: var(--color-gray); } .gallery01 .splide__pagination__page.is-active { background-color: var(--color-theme); } |
スライドが切り替わったらサムネイル画像を明るくする
1 2 3 4 5 6 7 | .gallery01 .splide-thumb .splide__slide:not(.is-active) { -webkit-transition: var(--transition); transition: var(--transition); opacity: 0.3; } |
アクティブスライドが切り替わると、is-activeが切り替わるのでそれを利用します。
スライドが切り替わったあとすぐにスライドを明るくするためには、updateOnMoveをtrueにしましょう。
falseだと、スライドが切り替わったあと少し遅延してスライドが明るくなります。
#08 応用 サムネイル(非スライダー)を付けて連動
先ほどのデモはサムネイルもスライダーになっていたので、メインスライダーが変わると、サムネイルの位置も変わりましたね。
そうではなく、このデモはサムネイルをスライダーにしません。
これはありがたいことに公式ドキュメントにそのままサンプルコードがありますのでそれを使用します。
詳しい説明は公式ドキュメントをご参照ください。
■参考
ギャラリー
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 | const main = '.gallery02 .splide'; const main_options = { pagination: false, } const mainSplide = new Splide(main, main_options); const thumbnails = document.querySelectorAll('.gallery02 .thumb-media'); let current; // 現在のサムネイルを保持するための変数 const initThumbnail = (thumbnail, index) => { thumbnail.addEventListener('click', () => { mainSplide.go(index); }); } for (let i = 0; i < thumbnails.length; i++) { initThumbnail(thumbnails[i], i); } mainSplide.on('mounted move', () => { if (current) { current.classList.remove('is-active'); } // Splide#indexは現在アクティブなスライドのインデックスを返す const thumbnail = thumbnails[mainSplide.index]; if (thumbnail) { thumbnail.classList.add('is-active'); current = thumbnail; } }); mainSplide.mount(); |
ちなみに、上記の26行目部分の「Splide#index」という意味についてですが、Splideインスタンスのメソッドのindexメソッドという意味です。
#09 基礎 スライダーの向きを交互に変える
このデモは、2つのスライダーが逆方向に無限ループするスライダーです。
これはそこまで難しくありません。
Splideには、スライドの向きを変更するオプションがありますので、それを利用すればOKです。
デモのポイント
スライダーの進行方向を逆にする
同じオプションをまとめる
ホバーしたときにスライドの横幅を大きくする
スマホサイズのときはスピードを変える
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 | const target_rtl = '.gallery03 .splide-a'; const target_ltr = '.gallery03 .splide-b'; //共通オプション const commonOptions = { type: 'loop', arrows: false, drag: 'free', flickPower: 300, pagination: false, autoWidth: true, autoHeight: true, autoScroll: { speed: 1, pauseOnHover: false, pauseOnFocus: true, }, breakpoints: { 600: { autoScroll: { speed: 0.3 } } } } // スライド方向:右から左 const rtlOptions = { ...commonOptions, direction: 'rtl' }; // スライド方向:左から右 const ltrOptions = { ...commonOptions, direction: 'ltr' }; const rtlSplide = new Splide(target_rtl, rtlOptions); rtlSplide.mount(window.splide.Extensions); const ltrSplide = new Splide(target_ltr, ltrOptions); ltrSplide.mount(window.splide.Extensions); |
スライダーの進行方向を逆にする
directionプロパティを‘rtl’にすると、右から左方向にスライドが移動します。
‘ltr’にすると、左から右へスライドが移動します。
この後説明する、共通オプションを管理している変数のcommonOptionsに、左方向用のオプションと、右方向用のオプションをそれぞれスプレッド構文を用いてマージしています。
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
direction | string | なし | スライダーの方向を指定します。 ‘ltr’左から右 ‘rtl’右から左 ‘ttb’上から下 |
ちなみに、‘ltr’は、「left to right」の略だと思われます。
同様に、‘rtl’は、「right to left」、
‘ttb’は、おそらく、「top to bottom」の略です。
同じオプションをまとめる
2つのスライダーは、スライドの向きが違うだけで、それ以外のオプションは共通です。
そのため、共通オプションとして変数に入れておくと管理がしやすいでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //共通オプション const commonOptions = { type: 'loop', arrows: false, drag: 'free', flickPower: 300, pagination: false, autoWidth: true, autoHeight: true, autoScroll: { speed: 1, pauseOnHover: false, pauseOnFocus: true, }, } |
上記のように、共通オプションをまとめておき、
次のように、JSのスプレッド構文を用いてマージしています。
1 2 3 4 5 6 7 8 9 10 11 12 | // スライド方向:右から左 const rtlOptions = { ...commonOptions, direction: 'rtl' }; // スライド方向:左から右 const ltrOptions = { ...commonOptions, direction: 'ltr' }; |
■参考
スプレッド構文
ホバーしたときにスライドの横幅を大きくする
このデモでは、スライドのwidthとheightをSplideのオプションで設定していません。
代わりに、autoWidthとautoHeightをtrueにしています。
このようにすることで、スライドのwidthとheightをCSSで調整することができるようになります。
CSSで調整しなくても、fixedWidthとfixedHeightというオプションがあるので、それを使いたい気持ちになるのですが、これを使うとスライドにホバーしたときに、スライドのwidthが伸びません。
そのため、今回はCSSで調整しています。
スマホサイズのときはスピードを変える
自動スクロールの流れるスピードの早さは、speedオプションの値で決まります。
デフォルトでは1ですが、この値を小さい値にすると遅くなります。
ただ、デフォルトの1だとスマホで見たときにとても早く感じます。
そんな時は、breakpointsオプションを使って、その中でautoScrollのspeedを上書きします。
このデモでは、600px以下でspeedを0.3にして遅くしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //共通オプション const commonOptions = { ⋮ autoScroll: { speed: 1, ⋮ }, breakpoints: { 600: { autoScroll: { speed: 0.3 } } } } |
#10 応用スライダーを入れ子にする
結論から言うと、これはSwiperで作った方がいいです。
Splideでも入れ子にできるのですが、親と子のスライド方向が同じ場合うまく動かないです。
Swiperだと、nested:trueとすることで、親と子のスライド方向が一緒の場合の誤作動を防ぐことができますが、Splideに同様のオプションがありません。
そのため、このデモでは子スライドはドラッグできないようにdrag: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 28 29 30 31 32 33 34 35 36 37 | const targetParent = '.flow01 .splide-main'; const targetChild = '.flow01 .splide-sub'; const parentOptions = { ⋮ classes: { arrows: 'splide__arrows splide__arrows-main', arrow: 'splide__arrow splide__arrow-main', prev: 'splide__arrow--prev splide__arrow-main--prev', next: 'splide__arrow--next splide__arrow-main--next', }, ⋮ } const childOptions = { ⋮ classes: { arrows: 'splide__arrows splide__arrows-sub', arrow: 'splide__arrow splide__arrow-sub', prev: 'splide__arrow--prev splide__arrow-sub--prev', next: 'splide__arrow--next splide__arrow-sub--next', }, drag: false, } const parentSplide = new Splide(targetParent, parentOptions); ⋮ parentSplide.mount(); const childElms = document.querySelectorAll(targetChild); childElms.forEach(i => { new Splide(i, childOptions).mount(); }); |
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | <div class="flow01 l-section"> <div class="l-inner"> <div class="splide splide-main" aria-label="親のスライダー"> <div class="splide-wrapper"> <ul class="splide__pagination splide__pagination-main"></ul> <div class="splide__track splide__track-main"> <div class="splide__list"> <div class="splide__slide"> <div class="mainslide"> <h2 class="mainslide-title">Lorem ipsum</h2> <div class="splide splide-sub" aria-label="子のスライダー"> <div class="splide-wrapper"> <div class="splide__track"> <div class="splide__list"> <div class="splide__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="splide__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> </div> <div class="splide__arrows splide__arrows-sub"></div> </div><!-- /splide-wrapper --> <div class="splide__pagination splide__pagination-sub"></div> </div><!-- /splide-sub --> </div> </div> <div class="splide__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="splide__slide"> <div class="mainslide"> <h2 class="mainslide-title">Ut enim</h2> <div class="splide splide-sub" aria-label="子のスライダー"> <div class="splide-wrapper"> <div class="splide__track"> <div class="splide__list"> <div class="splide__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="splide__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> <div class="splide__arrows splide__arrows-sub"></div> </div> </div><!-- /splide-wrapper --> <div class="splide__pagination splide__pagination-sub"></div> </div><!-- /splide-sub --> </div> </div> </div> </div> </div><!-- /splide-wrapper --> </div><!-- /splide-main --> </div> </div> |
このように、親スライダーと子スライダーをそのまま入れ子構造に、JSでそれぞれmountするだけで実装できます。
ただ、前述のとおり、子のスライダーにもページネーションを付けているため若干コード量が多くなっています。
ページネーションの出力内容をカスタマイズする
スライド上にある「STEP01」などのページネーションについてです。
まず、このページネーションを出力する位置の関係で、HTML上に以下の様に場所を指定しています。
1 2 3 4 5 6 7 8 9 10 11 | <div class="flow01 l-section"> <div class="l-inner"> <div class="splide splide-main" aria-label="親のスライダー"> <div class="splide-wrapper"> <!-- ここに出力 --> <ul class="splide__pagination splide__pagination-main"></ul> ⋮ |
そして、JSで調整を行います。
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 | let paginationLis; let current; // 現在のサムネイルを保持するための変数 const initLi = (li, index) => { li.addEventListener('click', () => { parentSplide.go(index); }); } parentSplide.on('pagination:mounted', function (data) { //ページネーションがマウントされた後じゃないとDOMを取得できないのでこの中で取得する。 paginationLis = document.querySelectorAll('.flow01 .splide__pagination-main > li'); for (let i = 0; i < paginationLis.length; i++) { initLi(paginationLis[i], i); } // `items`は、すべてのLI要素とボタン要素を保持しています data.items.forEach(function (item) { const num = ('00' + (item.page + 1)).slice(-2); item.button.insertAdjacentHTML('afterbegin', `<span class="splide-pagination-bullet"><span class="step">STEP.</span>${num}</span>`); }); }); parentSplide.on('mounted move', () => { if (current) { current.classList.remove('is-active'); } // Splide#indexは現在アクティブなスライドのインデックスを返す const li = paginationLis[parentSplide.index]; if (li) { li.classList.add('is-active'); current = li; } }); |
Splideのページネーションは公式ドキュメントにもある通り、以下のようなHTMLを出力します。
※一部抜粋
1 2 3 4 5 6 7 8 9 10 11 | <ul class="splide__pagination"> <li role="presentation"> <button class="splide__pagination__page is-active"></button> </li> <li role="presentation"> <button class="splide__pagination__page"></button> </li> ⋮ </ul> |
ご覧の通り、.is-activeが、liタグに付かず、buttonタグにつきます。
このデモでは、liタグにもis-activeを付与しないと実装が難しいので、スクリプトで書く必要があります。
具体的には、以下で実装しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | parentSplide.on('mounted move', () => { if (current) { current.classList.remove('is-active'); } // Splide#indexは現在アクティブなスライドのインデックスを返す const li = paginationLis[parentSplide.index]; if (li) { li.classList.add('is-active'); current = li; } }); |
結論、このデモに関しては、Swiperの方がコード量も少なく実装できるのでSwiperがおすすめです。
#11 基本メインビジュアル フェードアニメーション
各スライドはフェードで切り替わり、スライド内の画像はフェードイン、テキストはブラーがかかった状態から鮮明になるようなアニメーションを備えたデモとなっています。
デモのポイント
フェードアニメーション+ループスライダー
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const target = '.mv01 .splide'; const target_options = { type: 'fade', rewind: true, autoplay: true, arrows: false, interval: 7000, //1枚のスライドをどのぐらい表示させておくか(次のスライド表示まで何秒かけるか。)⇒Swiperのautoplayのdelayに相当 speed: 2000, //次のスライダーへの移動時間をミリ秒単位で指定(ミリ秒) } const mySplide = new Splide(target, target_options); mySplide.mount(); |
フェードモードにするためには、type:’fade’にする必要があります。
また、自動再生したいのでautoplay:trueにしています。
ただ、これだけだとスライダーがループしません。
そのため、rewindをtrueにします。
■使用しているオプション
オプション | タイプ | デフォルト値 | 説明 |
---|---|---|---|
type | string | ‘slide’ | スライダーの種類を指定します。 slide: 一般的なスライドアニメーションによるスライダー |
rewind | boolean | false | スライダーの終わりまで行ったときに、先頭に巻き戻せるかどうかを決定します。 |
interval | number | 5000 | 自動再生の間隔をミリ秒単位で指定します。 |
speed | number | 400 | スライダーの移動時間をミリ秒単位で指定します。 0を指定すると、アニメーションなしで直接対象に遷移します。 |
また、このデモでは画像がフェードインして、テキストがブラーがかかった状態から鮮明になるようなアニメーションをCSSで付けています。
具体的には、以下のようにして調整しています。
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 | .mv01 .slide-media img { transition: 7s 1s ease-out; } .mv01 .splide__slide[class*=-active] .slide-media img { transition-delay: 0s; transform: scale(1.05); } .mv01 .splide__slide[class*=-active] .slide-title { animation: mv01-fadeIn 2s 0.5s var(--easing) both; } @keyframes mv01-fadeIn { 0% { transform: scale(0.5); opacity: 0; filter: blur(300px); } 100% { transform: scale(1); opacity: 1; filter: blur(0); } } |
上記の7行目がポイントです。
これがないと画像のフェードインが、スライドが切り替わった際に変な挙動になってしまいます。
#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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | const myDelay = 7000; let timer; const switchAnimation = () => { clearTimeout(timer); let activeSlide = document.querySelectorAll('.mv02 .splide__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 .splide__slide.anm-started'); for (let i = 0; i < activeSlide.length; i++) { activeSlide[i].classList.remove('anm-started'); activeSlide[i].classList.add('anm-finished'); } } const target = '.mv02 .splide'; const target_options = { type: 'fade', rewind: true, autoplay: true, speed: 2000, //次のスライドに切り替わるまでの時間を指定(ミリ秒)Swiperだとautoplayのdelayに相当する interval: myDelay, //1枚のスライドをどのぐらい表示させておくか arrows: false, pauseOnHover: false, // マウスオーバーしたときに自動再生を止めるかどうか(false:止めない) pauseOnFocus: false, //スライダー内にフォーカスされたエレメントがある場合、自動再生を止めるかどうか。推奨はtrue。 } const mySplide = new Splide(target, target_options); mySplide.on('move mounted', function () { //スライダーが動く直前とスライダー生成時に発生 finishAnimation(); }); mySplide.on('active', function () { //アクティブスライドが変わったときに発生 switchAnimation(); }); mySplide.mount(); |
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 | .mv02 .slide-media img { transition: 7s 1s linear; transform: translateX(-1.5%) scale(1.1); } .mv02 .slide-title, .mv02 .slide-text, .mv02 .slide-link { animation: 2s var(--easing) both; opacity: 0; } .mv02 .splide__slide[class*=-active] .slide-media img { transition-delay: 0s; transform: translateX(1.5%) scale(1.05); } .mv02 .splide__slide.anm-started .slide-title, .mv02 .splide__slide.anm-started .slide-text, .mv02 .splide__slide.anm-started .slide-link { animation-name: mv02-fadeIn; } .mv02 .splide__slide.anm-finished .slide-title, .mv02 .splide__slide.anm-finished .slide-text, .mv02 .splide__slide.anm-finished .slide-link { animation-name: mv02-fadeOut; } @keyframes mv02-fadeIn { 0% { transform: translateY(6rem); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes mv02-fadeOut { 0% { transform: translateY(0); opacity: 1; } 100% { transform: translateY(-6rem); opacity: 0; } } |
CSSであらかじめ.anm-startedとanm-finishedが付いたときの挙動を記載しておきます。
CSSを見ての通り、.anm-startedが付いたときに、タイトルやテキスト、リンクといったコンテンツにmv02-fadeInというアニメーションが実行されるようになっていますね。
mv02-fadeInは、初期状態では画面下方向に6rem移動した位置にあり、アニメーション完了状態では元の位置に戻るようなアニメーションとなっています。
一方、.anm-finishedが付いたときに実行されるアニメーションである、mv02-fadeOutは、初期状態では元の位置にあり、アニメーション完了状態では画面上方向に6rem移動した位置になるようなアニメーションとなっています。
このようにアニメーションを作っておき、あとはJSでこれらのクラスを特定のタイミングで付けたり外したりすればいいわけですね。
SwiperのslideChangeイベントに相当するSplideのイベントがmoveで、slideChangeTransitionStartに相当するSplideのイベントがactiveです。
#13 応用 縦方向のスライダー
縦方向スライダーもたまに見かけますよね。
実装する際の注意点としては、.splide__track への高さ指定が必須となります。
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 | const thumb = '.mv03 .splide-thumb'; const thumb_options = { mediaQuery: 'min', gap: 0, arrows: false, pagination: false, isNavigation: true, perPage: 4, direction: 'ttb', heightRatio: 1.25, //横幅480に対して縦600(directionをttbにしているので600 ÷ 480) breakpoints: { 600: { gap: 8 }, 1025: { gap: 16 }, }, } const thumbSplide = new Splide(thumb, thumb_options); const main = '.mv03 .splide-main'; const main_options = { type: 'loop', autoplay: true, updateOnMove: true, //is-activeクラスを移動前に更新する。 speed: 2000, //次のスライドに切り替わるまでの時間を指定(ミリ秒)Swiperだとautoplayのdelayに相当する pagination: false, arrows: false, direction: 'ttb', //スライダーの方向を指定 drag: false, height: 800, breakpoints: { //max-width:1024pxのときの指定 1024: { height: 'clamp(400px,70vh, 600px)', }, }, } const mainSplide = new Splide(main, main_options); mainSplide.sync(thumbSplide); mainSplide.mount(); thumbSplide.mount(); |
スライダーの方向を縦方向にするために、directionを‘ttb’にしています。
これは「top to bottom」の略です。(おそらく)
縦方向にした場合、スライダーの高さを指定する必要があります。
このデモでは、サムネイルのスライダーに関しては、heightRatioを1.25にして指定しています。
メインスライダーに関しては、heightを800にして、ビューポートが1024px以下の場合はheight: ‘clamp(400px,70vh, 600px)’としています。
このように、heightを相対値で指定できたり、CSSの比較関数(この例だとclamp)を指定できるのでかなり指定が楽な印象です。
これらのheightに関する指定は、DOMのsplide__trackに指定されるようになっているので、詳しくは検証ツールでご確認ください。
また、メインスライダーとサムネイルスライダーを連動させるために、syncを使っています。
これは#07のデモで登場しましたのでここでは説明を割愛します。
#14 応用 スライドごとに表示時間を変えて、進捗状況と連動したプログレスバーを作成する
スライドごとに表示時間を変えたい場合もあるでしょう。
ただ、それだけではなく、プログレスバーなどのアニメーションも、スライドの表示時間に合わせたいはずです。
デモのポイント
スライドごとに表示時間(delay)を変える
進捗状況と連動したアニメーション(プログレスバー)
スライドごとに表示時間(delay)を変える
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 | <div class="splide"> <div class="splide-wrapper"> <div class="splide__track"> <div class="splide__list"> <div class="splide__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="splide__slide" data-splide-interval="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> ⋮ </div> </div> </div><!-- /splide-wrapper --> </div><!-- /splide --> |
通常、スライドの表示時間はintervalを指定します。
個別に表示時間を設定したいときは、.splide__slideにdata-splide-intervalを指定します。
値の単位はミリ秒です。
■参考
interval
進捗状況と連動したアニメーション(プログレスバー)
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 | const myDelay = 7000; const slide = document.querySelectorAll('.mv04 .splide__slide'); const slideLength = slide.length; 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++) { let delay = Number(slide[i].getAttribute('data-splide-interval')); slideDelay[i] = delay ? delay : myDelay; 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 target = '.mv04 .splide'; const options = { type: 'fade', rewind: true, autoplay: true, speed: 1000, interval: myDelay, arrows: false, pagination: false, pauseOnHover: false, // マウスオーバーしたときに自動再生を止めるかどうか(false:止めない) pauseOnFocus: false, //スライダー内にフォーカスされたエレメントがある場合、自動再生を止めるかどうか。推奨はtrue。 } const mySplide = new Splide(target, options); // バーの幅を更新する mySplide.on('mounted', function () { updateProgress(0); }); mySplide.on('move', function () { updateProgress(mySplide.index); }); mySplide.mount(); |
スライド下にあるプログレスバーは、スライダー全体の進捗率を表しており、progressbarで表現しています。
円形ラインは、各スライドの進捗率を表しており、.slide-fractionで表現しています。
スライダーの表示時間と連動させるために、以下で調整を行っています。
1 2 3 4 5 6 7 8 9 10 | const slideDelay = []; for (let i = 0; i < slideLength; i++) { let delay = Number(slide[i].getAttribute('data-splide-interval')); slideDelay[i] = delay ? delay : myDelay; 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; } |
このあたりの知識は以下の記事を参考に理解を深めてみてください。
HTMLとCSSで作るインフォグラフィック(円/棒グラフ・レーダーチャート)【アニメーションつき】
ただ、Splideで作った場合のデメリットは、.slide-titleにmin-heightをつけないと、各スライドで高さがちぐはぐになってしまう点です。
Swiperの場合は、fadeモードにしても、display:flex;が指定された親要素の中に各スライドが配置されるので、各スライドの中身が異なっていたとしても、一律に高さが揃います。
しかし、Splideの場合はそうなりません。
なので、たとえば.slide-titleにmin-heightを指定しないと
以下のような挙動になります。
スマホサイズにしてご確認ください。
min-heightを指定しない場合
このようにちぐはぐなスライドになるので、min-heightを指定する必要があります。
そのため、.slide-titleの文章量には当然制限が生まれ、ある行数以上は3点リードにするなどの対策が必要になります。
まとめ
Splideが実務でも十分につかえるのか?という観点で、Swiperの記事で実装しているデモをSplideに置き換えてみました。
結論、Swiperで実装したほうがいいデモは以下の3つでした。
・#03 基本 コンテンツ幅からはみ出たスライドを薄くする
・#07 基本 サムネイル(スライダー)を付けて連動させる
・#10 基本 スライダーを入れ子にする
そして、Swiperの記事では実装していた15個目のデモは、Splideでは実装できませんでした。
改めてSwiperの凄さを実感したとともに、Splideでも十分実務で使えると感じました。
Splideの魅力は、やはり容量がめっちゃ軽いことです。
今やサイトスピードもSEOに関わってきます。
SEOだけでなく、ユーザー側も当然、サイトがすぐ表示された方が好印象をもつでしょう。
そのため、Splideで実装できるならSplideを選択し、そうでないならSwiperという選択が良さそうだと感じました。
SplideはGitHubでIssuesが立てられ、その後何週間かしたあとアップデートされていますので開発が盛んです。
今後もおそらく新しいオプションなどが追加されると思いますので注視していきたいと思います。
『まだslick使っている』という方はぜひ、この記事を通してSplideを使ってみてはいかがでしょうか。
どうしても実装できなかったパララックスのデモ
最後に、Swiperの記事で実装していたパララックスのデモ、これがなぜSplideでできなかったのか簡単に触れておきます。
このデモの仕組みは、前のスライドおよび次のスライドの画像を、現在アクティブなスライドの中央側にtransform:translateXで移動させておき、スライドの箱(div.slide)の移動距離よりも画像の移動距離を小さくすることで奥行を感じさせてパララックスを演出しています。
ただ、Splideの場合、スライドの箱(div.slide)と画像が一緒に動いてしまい以下の動画のようになってしまいました。。
動画を見ていただくとわかるように、最初に次のスライドのimgがアクティブなスライドの中央側に寄っています。
次に、スライドが動くと同時にimgも左方向に一緒に動き、ぬるっと元の位置に戻ります。そのため右端に隙間ができて変な動きになっています。
Swiperのデモのほうでは、imgはスライドの箱と一緒に動かず、残りの移動距離分だけしか動かないので右端に隙間ができません。
このSplideのデモに関しては色々試したのですがどうもうまくという感じでギブアップしてしまいました。。