jQueryでカルーセルパネルを実装する勉強をしたので、備忘録がてら残しておきます。
カルーセルパネルはjQueryのコンテンツの中でも難易度の高いものです。
しかし、一度作って理解しておけば流用もできるでしょう。
今回のポイントは「レベルの低い方から、順に」です。
一気に完成形を目指すと混乱するので、少しづつ実装していきましょう。
ちなみに完成形はこんな感じ
実装する機能としては
- 自動で右に切り替わる
- 一番右、一番左まで行ってもループする
- 進むボタン、戻るボタンで切り替わる
- 下にインジケーターを表示して、クリックしても切り替わる
- 画像をドラッグすることでも切り替わる
です。これでカルーセルパネル(スライド)の機能はほぼすべてだと思います。
実装する機能を選びながら、参考にしてください。
完成形のコードは一番下に記載します。
jQueryでカルーセルパネル(スライダー)を作る
jQueryでカルーセルパネルー1:まずは画像を並べる
jQueryでコーディングする前に、まずはHTMLとCSSを整えましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<section class="hero" id="hero"> <div class="slideshow"> <div class="slide-screen"> <div class="slideshow-slides"> <div class="slide"><img src="images/B-016.jpg" /></div> <div class="slide"><img src="images/B-017.jpg" /></div> <div class="slide"><img src="images/B-018.jpg" /></div> <div class="slide"><img src="images/B-019.jpg" /></div> <div class="slide"><img src="images/B-020.jpg" /></div> <div class="slide"><img src="images/B-021.jpg" /></div> </div> </div> <div class="slideshow-nav"> <a href="#" class="prev"><img src="images/prev-icon.png" width="56" alt=""/></a> <a href="#" class="next"><img src="images/next-icon.png" width="56" alt=""/></a> </div> <div class="slideshow-indicator"></div> </div> </section> |
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 |
.slideshow{ background-color: rgb(0,0,0); height:200px; position:relative; width:100%; margin:auto; } .slide-screen{ position:relative; height:200px; left:50%; transform: translateX(-50%); -webkit- transform: translateX(-50%); width:300px; overflow: visible; } .slideshow-slides{ position:absolute; height:200px; width:1000%; } .slideshow-slides .slide{ float:left; cursor: pointer; } .slide img{ width:300px; } /* ナビゲーション */ .slideshow-nav a{ position:absolute; top:50%; margin-top:-28px; } .slideshow-nav a.prev{ left:0%; margin-left: 10px; } .slideshow-nav a.next{ right:0%; margin-right:10px; } .slideshow-nav a.disabled{ display:none; } /* インジケーター */ .slideshow-indicator{ position:absolute; text-align: center; right:0; left:0; bottom:0; } .slideshow-indicator a{ display:inline-block; margin:0 3px; width:10px; height:10px; background-color:#fff; opacity: 50%; border-radius: 50%; } .slideshow-indicator a.active{ cursor:default; opacity: 100%; } |
行数は多いですが、やっていることは単純です。
まずHTMLですが、それぞれのクラスの役割は以下の通りです。
- slideshow:カルーセルパネルの全体
- slide-screen:カルーセルの画面
- slideshow-slides:スライドの帯
- slide:各種スライド
- slideshow-nav:進む、戻るのナビゲーション
- slideshow-indicator:下部に表示されるインジケーター
カルーセルパネルの仕組みですが、実際には以下の画像のような形になっています。
カルーセルパネルは、画像を横に並べた帯状の集合があり(slideshow-slides)、それを右や左に動かすことで、画像を切り替えています。
slideshow-screenにはcssで「overflow:hidden」を設定し、見える部分以外を非表示にしています。
今回は見えやすいようにわざと「overflow:visible」を指定します。
最後にはこの設定は消しますが、アニメーションの動きが分かりやすくなるために入れています。
cssの設定は見栄え程度なので、詳細の説明は省きます。ここまでで、以下のようになるはずです。
ヘッダーやフッターは気にしなくてOKです。
画像が横方向に繋がっているのが確認できますね。
jQueryでカルーセルパネルー2:アニメーションで右に移動する
さて、ここからがjQueryとなります。
まずはjQueryを用いて、スライドの帯を動かしていきましょう。
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 |
$(function () { /* * Slideshow */ function slider() { //変数の設定 var $sliderWrap = $('.slideshow'), $slider = $sliderWrap.find('.slideshow-slides'), $slides = $slider.find('.slide'), currentIndex = 0, slidewidth = $slides.width(), timer; //nextボタンの処理 function nextSlide() { if(!$slider.is(":animated")){ currentIndex++; changeSlide(); } } //スライドアニメーション function changeSlide() { var duration = 500; $slider.animate({ left: (currentIndex + 1) * -(slidewidth) + "px" },duration); } //タイマースタート function startTimer() { var interval = 1000; timer = setInterval(nextSlide, interval); } startTimer(); } slider(); }); |
少しづつ見ていきましょう。
まず、それぞれのdiv要素を変数に入れています。
currentIndexは現在のスライド番号です。
nextSlide関数は、currentIndexに+1して、スライドアニメーション関数を呼んでいます。
スライドアニメーション関数は、スライドの帯を左に動かしています。
animate関数を使用しているので、緩やかに左へと動きます。
かかる時間は0.5秒で、duration関数で操作可能です。
最後にタイマーをセットしています。
1000ミリ秒(1秒)ごとにnextSlide関数を呼び出します。
これで1秒ごとにスライドが左に移動していきます。
最初に2つ分移動しますが、仕様なので今は気にしないでください。
このままでは永遠に左に移動してしまいます。
全スライドを移動したら、先頭に戻るようにしましょう。
jQueryでカルーセルパネルー3:スライドをループさせる
左に行き過ぎないように、jsファイルを編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//変数の設定 slideLength = $slides.length, //スライドアニメーション function changeSlide() { //省略 if(currentIndex == slideLength -1 ){ currentIndex = 0; $slider.animate({ left: (currentIndex + 1) * -(slidewidth) + "px" },duration); } } |
スライドアニメーションに条件を追加します。
スライドの枚数に到達したら、最初のスライドに戻ります。
slideの枚数を取得するために、変数設定にslideLengthを追加しましょう。
なお、slideLength – 1を設定していますが、これはスライドのカウントが0から始まるためです。
これで、ループが完成します。
一応ループはしますが、急に巻き戻る感じですね。
次はこれを直しましょう。
jQueryでカルーセルパネルー4:滑らかにループさせる
スライドを滑らかにループさせるには、ちょっとしたコツが要ります。
イメージは以下の画像です。
右にループさせる場合は、スライドの帯内に、最初のスライドのコピーを配置します。
そしてコピーしたslide1までは滑らかに移動します。
コピーしたslide1まで移動した後に、裏でcurrentIndexを0に戻します。
コピーしたslide1まで移動した後に、また再度新しいスライドの帯に移動している感じです。
追加するjsは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//スライドクローン function cloneSlide(){ var $lastSlide = $slider.find('div:last-child'), $firstSlide = $slider.find('div:first-child'); $lastSlide.clone(true).prependTo($slider); $firstSlide.clone(true).appendTo($slider); } //スライドアニメーション function changeSlide() { //省略 if(currentIndex == slideLength){ //-1を削除 currentIndex = 0; $slider.animate({ left: (currentIndex + 1) * -(slidewidth) + "px" },0); //0に変更 } } |
スライドを複製して、アニメーションにも少し変更を加えています。
最初のスライドの前に最後のスライドを(0番)、最後のスライドの後に最初のスライドを(slideLength番)追加します。
これに伴い、ループの場所を調節するために「-1」を消しています。
また、一瞬で次スライドの帯へと移るために、durationは0へと変更します。
これでアニメーションなしで次のスライドの帯へと移動します。
当然ですが、cloneSlide関数はページ下部のstartTimer関数の前くらいで呼んでおいてください。
スムーズにループしてるのが分かります。こんな風にループしてるんですね。
スライドの動きを作ってしまえば、後はさまざまな機能を実装するだけです。
よく考えてみればわかりますが、以下の機能は結局のところスライドを動かしているだけだったりします。
- 進む、戻るボタン
- インジケータークリック
- ドラッグで動かす
それぞれを細かく見ていきましょう。
jQueryでカルーセルパネルー5:進むボタン、戻るボタンで移動する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//変数の設定 //省略 $prev = $sliderWrap.find('.prev'), $next = $sliderWrap.find('.next'), //prevボタンの処理 function prevSlide() { if(!$slider.is(":animated")){ currentIndex--; changeSlide(); } } //カルーセルイベント一覧 function setEvent() { $prev.on('click', prevSlide); $next.on('click', nextSlide); } setEvent(); |
進むボタンの処理は既に書いているので、同様に戻るボタンの処理も書きます。
currentIndexに対して-1するだけです。楽ですね。
あとはこの処理をカルーセルイベントとして登録しています。
ここから先、クリックやドラッグでのカルーセル操作はここにまとめます。
全処理の後半にsetEventを呼び出すことも忘れないようにしましょう。
また、自動アニメーションの時間が1秒だとスライドが切り替わりすぎてしまいます。
0を一つ足して、10秒に一回自動で移動するように設定しておきましょう。
さて、続いてスライド移動処理に、戻るボタンでループする処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//スライドアニメーション function changeSlide() { var duration = 500; $slider.animate({ left: (currentIndex + 1) * -(slidewidth) + "px" },duration); if(currentIndex == slideLength){ currentIndex = 0; $slider.animate({ left: (currentIndex + 1) * -(slidewidth) + "px" },0); }else if(currentIndex == -1){ currentIndex = slideLength - 1; $slider.animate({ left: (currentIndex + 1) * -(slidewidth) + "px" },0); } } |
最終のスライドアニメーションとなります。
スライド番号が-1になったときに、番号を最後のスライドへと戻しています。
あとはこれまでと同じですね。
実際にボタンを押してみて、移動するかどうか確認してみてください。
jQueryでカルーセルパネルー6:インジケーターで移動する
インジケーターは、スライドの下に表示します。
今自分がどのスライドを見ているのか分かる機能です。
今回は白い丸で表示しましたが、線で表示されることもあります。
このインジケーターはクリックで、対象のスライドへと移動します。
この処理も実装していきましょう。
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 |
//変数宣言 //省略 $indicator = $sliderWrap.find('.slideshow-indicator'), $item = '', //インジケーターの生成 function addIndicator(){ var indicatorHTML = '<a href="#" class="item"></a>'; for (let i = 0; i < slideLength; i++){ $indicator.append(indicatorHTML); } $item = $(".item"); $item.eq(currentIndex).addClass("active"); } //現在のスライド位置をインジケーターに表示 function updateNav() { $item.removeClass("active"); $item.eq(currentIndex).addClass("active"); } //インジケータークリック処理 function goSomeWhere() { if(!$slider.is(":animated")){ currentIndex = $(this).index(); changeSlide(); } } //スライドアニメーション function changeSlide() { //省略 //インディケーターを更新 updateNav(); } //カルーセルイベント一覧 function setEvent() { $prev.on('click', prevSlide); $next.on('click', nextSlide); $item.on('click', goSomeWhere); } cloneSlide(); addIndicator(); startTimer(); setEvent(); |
少し長めですが、それには理由があります。
HTMLを見ると分かりますが、インジケーターは記載していません。
画像が増えた場合に、自動でインジケーターの数を増やすためです。
そのためまずはaddIndicatorでインジケーターを作成してHTMLに挿入します。
事前にa要素を作成し、画像の数だけループしてHTMLを作っています。
このインジケーターはクリック時に動くため、要素全体を$itemに上書きしています。
また現在のスライドにはactive属性を加えています。
eqを用いて、現在のスライド番号のa要素にのみactiveクラスをつけています。
スライドが移動したときに、インジケーターのactiveクラスを移動させています。
処理は簡単で、いったんactiveクラスを全削除、その後現在のスライド番号にactiveクラスをつけているだけです。
ちなみにこのインジケーター切り替えは進むボタンなどを押したときも機能します。
それゆえに、スライド移動関数の最後にも呼び出しています。
インジケーターをクリックしたときの関数です。
こちらも処理は簡単で、インジケーターの番号を取得してスライド移動関数を呼んでいるだけです。
indexはa要素の順番を意味します。3番目のインジケーターをクリックしたら3が取得できます。
インジケーターの順番と画像の順番はリンクしているので、これで対象の番号へと移動できます。
あとはカルーセルイベント関数や、初期段階で呼び出すだけです。
jQueryでカルーセルパネルー7:画像をドラッグすることで切り替える
この段階でカルーセルパネルとして機能は十分です。
ですがここでマウスのドラッグでも画像を切り替えられるようにしましょう。
仕様としては
- マウスを左にドラッグして放すと進む
- 右にドラッグして放すと戻る
- マウスの移動距離が短い(画像の半分以下)の場合は移動しない
です。
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 |
//変数の設定 //省略 drag_flg = false, posX, //マウスの位置 imgPosX, //要素の位置 drug_dis; //ドラッグ距離 //スライダークリック時 function clickSlider(evt1){ //ドラッグ中にする drag_flg = true; //クリック位置取得 posX = evt1.screenX; //要素の位置も取得 imgPosX = $slider.position(); } //スライダークリック解除時 function leaveSlider(){ if(drag_flg == true){ //ドラッグ量が画像の半分以上なら、次スライドへ移動 if(drug_dis < -(slidewidth / 2)){ nextSlide(); }else if(drug_dis > (slidewidth / 2)){ //半分以下なら元のスライドに戻す prevSlide(); }else{ //半分以下なら元のスライドに戻す changeSlide(); } //フラグリセット drag_flg = false; } } //スライダー移動時 function moveSlider(evt2){ if(drag_flg == true){ evt2.preventDefault(); $slider.css("left",(imgPosX.left + evt2.screenX - posX) + "px"); //ドラッグ距離を保存 drug_dis = evt2.screenX - posX } } //カルーセルイベント一覧 function setEvent() { $(window).on('mousemove',moveSlider); $(window).on('mouseup',leaveSlider); $slider.on({ mouseenter: stopTimer, mouseleave: startTimer, mousedown : clickSlider }); |
マウスでどれだけ動かしたかを判断するために、変数を追加しています。
それぞれの内容についてはコメントアウトを参照してください。
関数は「マウスクリック」→「マウス移動」→「マウスクリック放す」の順で紹介します。
「マウスクリック」はスライド上でクリックした場合、
その他はスライドから外れたとしても機能するようにしています。
$(window)で宣言しているので、ブラウザ外で放したとしても、機能します。
クリックした段階で、ドラッグのフラグをONにします。
クリックをした座標と、スライドの座標を保存しています。
ドラッグフラグがONならば処理を行います。
最初のevt2.preventDefault()は画像のドラッグ処理を無効化しています。
これを記載しないと禁止マークが表示されてしまい、挙動がおかしくなります。
ドラッグ量に応じてスライダー帯のleft値を調整しています。
元々の値とクリック時の値、そして動かした距離で動かしています。
最後にマウスを動かした距離を算出しています。
マウスクリックを離したときに機能します。
もちろんドラッグフラグがONでなければ動きません。
処理がやや多く、マウスの移動距離が画像の半分以上なら前か後のスライドへ移動します。
前か後かは-が付くかどうかで判別可能です。
もしもマウスの移動距離が半分以下の場合は、現在のスライドに緩やかに戻ります。
最後にドラッグフラグをOFFにして処理終了です。
jQueryでカルーセルパネル(スライダー)を作る ~おわりに~
多少調整の余地ありですが、ほぼほぼ形にはなったはずです。
流用することで、カルーセルパネルを作ることができます。
今回は画像のスライドでしたが、もちろん文字なんかも入れられます。
動きを理解しておくと、カスタマイズもしやすいです。
jsファイルの全容は以下に格納しておきます。
- クリックでjsファイルの全体表示
-
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161$(function () {/** Slideshow*/function slider() {//変数の設定var $sliderWrap = $('.slideshow'),$slider = $sliderWrap.find('.slideshow-slides'),$slides = $slider.find('.slide'),$indicator = $sliderWrap.find('.slideshow-indicator'),$prev = $sliderWrap.find('.prev'),$next = $sliderWrap.find('.next'),$item = '',currentIndex = 0,slideLength = $slides.length,slidewidth = $slides.width(),timer,drag_flg = false,posX, //マウスの位置imgPosX, //要素の位置drug_dis; //ドラッグ距離//スライドクローンfunction cloneSlide(){var $lastSlide = $slider.find('div:last-child'),$firstSlide = $slider.find('div:first-child');$lastSlide.clone(true).prependTo($slider);$firstSlide.clone(true).appendTo($slider);}//インジケーターの生成function addIndicator(){var indicatorHTML = '<a href="#" class="item"></a>';for (let i = 0; i < slideLength; i++){$indicator.append(indicatorHTML);}$item = $(".item");$item.eq(currentIndex).addClass("active");}//現在のスライド位置をインジケーターに表示function updateNav() {$item.removeClass("active");$item.eq(currentIndex).addClass("active");}//インジケータークリック処理function goSomeWhere() {if(!$slider.is(":animated")){currentIndex = $(this).index();changeSlide();}}//nextボタンの処理function nextSlide() {if(!$slider.is(":animated")){currentIndex++;changeSlide();}}//prevボタンの処理function prevSlide() {if(!$slider.is(":animated")){currentIndex--;changeSlide();}}//スライドアニメーションfunction changeSlide() {var duration = 500;$slider.animate({left: (currentIndex + 1) * -(slidewidth) + "px"},duration);if(currentIndex == slideLength){currentIndex = 0;$slider.animate({left: (currentIndex + 1) * -(slidewidth) + "px"},0);}else if(currentIndex == -1){currentIndex = slideLength - 1;$slider.animate({left: (currentIndex + 1) * -(slidewidth) + "px"},0);}//インディケーターを更新updateNav();}//タイマースタートfunction startTimer() {var interval = 10000;timer = setInterval(nextSlide, interval);}//タイマーの一時停止function stopTimer() {timer = clearInterval(timer);}//スライダークリック時function clickSlider(evt1){//ドラッグ中にするdrag_flg = true;//クリック位置取得posX = evt1.screenX;//要素の位置も取得imgPosX = $slider.position();}//スライダークリック解除時function leaveSlider(){if(drag_flg == true){//ドラッグ量が画像の半分以上なら、次スライドへ移動if(drug_dis < -(slidewidth / 2)){nextSlide();}else if(drug_dis > (slidewidth / 2)){//半分以下なら元のスライドに戻すprevSlide();}else{//半分以下なら元のスライドに戻すchangeSlide();}//フラグリセットdrag_flg = false;}}//スライダー移動時function moveSlider(evt2){if(drag_flg == true){evt2.preventDefault();$slider.css("left",(imgPosX.left + evt2.screenX - posX) + "px");//ドラッグ距離を保存drug_dis = evt2.screenX - posX}}//カルーセルイベント一覧function setEvent() {$(window).on('mousemove',moveSlider);$(window).on('mouseup',leaveSlider);$slider.on({mouseenter: stopTimer,mouseleave: startTimer,mousedown : clickSlider});$prev.on('click', prevSlide);$next.on('click', nextSlide);$item.on('click', goSomeWhere);}addIndicator();cloneSlide();setEvent();startTimer();}slider();});