【プラグインなし】カルーセルスライダーの作り方を解説(jQuery)

【プラグインなし】カルーセルスライダーの作り方を解説(jQuery)

この記事を書いた人

だいち

PENGIN BLOGメディア編集長。Web業界とは異業種の仕事をしながら、独学でWeb制作の世界に。副業でHP制作やコーディング代行、個人ブログの運営などに取り組み、現在はPENGINにてWebライティングやディレクションをしつつ、メディア運営全般を担当しています。(個人運営ブログ:https://daib-log.com/ )

今回はカルーセルスライダー(カルーセルパネル)の作り方をまとめます。

jsプラグインで簡単に実装できてしまうUIではありますが、思う挙動にならなかったり、バグがあった時などが大変です。

自分で記述したコードなら、何かあった時の対処も行いやすくなりますので、コーディングを学んでいる方は自作方法も知っておいたほうがいいと思いまとめました。

なお、今回実装するカルーセルスライダーは下記3点の要素を入れます。

  • スライドを前後に進めるボタン
  • ページネーション(スライドネーション)
  • 3秒毎の自動スクロール

かなり簡易的な内容ではありますが、カルーセル実装時の基礎的な考え方は知れるはずです。いつものようにコードを記載しながら解説していきますのでよければ参考にしてみてください!

※フェードインでのスライダー実装方法もまとめてるので、別パターンを探してる方はこちらの記事も参考にしてみてください。

カルーセルとは

カルーセルとは、回転台、回転木馬、回転コンベア、回転棚などの意味を持つ英単語。Webページなどに設けられる画像などの表示領域で、内容を左右に移動して切り替えられるものをこのように呼ぶ。

IT用語辞典ーカルーセルパネル

こんな感じでパネルが横にスクロールして表示されるスライドのことです。

汎用的なデザインで多くのWebサイトやアプリに採用されているUIではありますが、導入に当たってメリット・デメリットもあります。

メリット

  • 複数の情報をまとまったセクション内で発信できる
  • サイト全体のスペースを占領しない
  • 自動ループのスライダーであれば、サイトにダイナミックな動きを与えることができユーザーの注意をひくことができる

デメリット

  • 2枚目以降のスライドがみられない可能性がある
  • 全て確認するためにはユーザー操作が必要になる(タイマー設定が無い場合)

参考サイト
>カルーセルのUIは売り上げにつながるのか!? カルーセルデザインを考えるときに知っておきたい7つのこと

使い所はしっかり考える必要があるUIということですね。

ということで今回作成するデモを用意していますので、こちらを元に解説していきます。

スライド、スライドレール、表示枠の準備

まずはカルーセルスライドを実装するには配置イメージを理解することが必要です。

<div class="carousel">
  <ul class="carousel-area">
    <li class="carousel-list"><img class="carousel-img" src="img/hamster.jpg" alt="ハムスターの画像"></li>
    <li class="carousel-list"><img class="carousel-img" src="img/sheep.jpg" alt="羊の画像"></li>
    <li class="carousel-list"><img class="carousel-img" src="img/turtle.jpg" alt="亀の画像"></li>
    <li class="carousel-list"><img class="carousel-img" src="img/bird.jpg" alt="鳥の画像"></li>
    <li class="carousel-list"><img class="carousel-img" src="img/lion.jpg" alt="ライオンの画像"></li>
  </ul>
</div>

ここから上のHTMLを下の図のような配置にしていきます。

スライドclassイメージ
classイメージ
  • carousel(黒)      ・・・表示枠
  • carousel-area(オレンジ)・・・スライド全体が乗っかるレール
  • carousel-list(青)     ・・・imgを格納するリスト1枚

スライド画像を横並びに配置させる

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  list-style: none;
}
.carousel {
  width: 600px;
  height: calc(600px * 0.5625);
  margin: 0 auto;
}
.carousel-area {
  width: 3150px;
  height: 100%;
  display: flex;
}
.carousel-list {
  width: 600px;
 height: calc(600px * 0.5625);
  margin-right: 30px;
}
.carousel-img {
  width: 100%;
  height: 100%;
}
@media screen and (max-width: 600px) {
  .carousel {
    width: 300px;
    height: calc(300px * 0.5625);
  }
  .carousel-area {
    width: 1500px;
  }
  .carousel-list {
    width: 300px;
    height: calc(300px * 0.5625);
    margin-right: 0;
  }
}

.carouselと.carousel-listの縦横サイズは同じにしています。.carousel-listの中のimgもですね。

.carousel-areaの数値は(.carousel-listの幅+margin)×スライド枚数です。

計算例
  1. width:600px + margin-right:30px = 630px
  2. 630px × 5 = 3,150px ※SPサイズも計算方法は同じです

また、横並びはdisplay: flexでしています。

ここまでで下のデモのような横スクロールリストになっています。

スライドが横並びにになったのを確認したら全体にoverflow: hiddenではみ出た部分は非表示にしておきましょう。

body {
  overflow: hidden;
}

imgタグ画像を非表示、backgroundで画像表示

※スライダー実装とは関係ない内容なので、ご興味無い方はここのセクションは飛ばしてください。

ここで一つ本題とは関係ない部分で問題があるんですが、今回使用した画像はサイズが統一されていない為画像の縮尺に違和感があります。

上記コードからimgタグのheight: 100%を削除すると実際の縮尺が表示され、下画像のようにバラ付きがあることがわかります。

画像サイズにバラ付きがある画像

そこで今回はimgタグではなく、縮尺(表示)調整が容易なCSSのbackgroundで画像は表示させます。※object-fitでもいいんですがIE対応の補足まで入れると長くなるので今回はbackgroundでのやり方で。

ということでCSSに下記を追記。

.carousel-list {
  width: 600px;
  height: 100%;
  margin-right: 30px;
  background-size: cover;     /* 追記 */
  background-position: center;  /* 追記 */
  background-repeat: no-repeat;  /* 追記 */
}
/* 以下、各スライドの背景に画像設定 */
.carousel-list:nth-child(1) {
  background-image: url(./img/hamster.jpg);
}
.carousel-list:nth-child(2) {
  background-image: url(./img/sheep.jpg);
}
.carousel-list:nth-child(3) {
  background-image: url(./img/turtle.jpg);
}
.carousel-list:nth-child(4) {
  background-image: url(./img/bird.jpg);
}
.carousel-list:nth-child(5) {
  background-image: url(./img/lion.jpg);
}
/* clipで非表示指定(スクリーンリーダー対策) */
.carousel-img {
  width: 1px;  /* 100% → 1px */
  height: 1px; /* 100% → 1px */
  clip: rect(1px, 1px, 1px, 1px);
  -webkit-clip-path: inset(50%);
  clip-path: inset(50%);
  margin: -1px;
  padding: 0;
  overflow: hidden;
  position: absolute;
}

HTMLのimgタグを消してもよかったんですが、そうすると当然スクリーンリーダーに画像の存在が読み込まれません。

このスライド画像がコンバージョンに繋がるバナーリンク画像だったり、コンテンツの中で重要なスライドであれば、アクセシビリティも考慮してimgタグは残しておきたいです。

ただ、display: noneやvisibility: hiddenで消すとスクリーンリーダーには読み込まれません。ということでclipで非表示にするテクニックを使っています。

ともかく、これで画像も良い感じで表示されました。

ページ送りのボタンを設置

次にクリックするとページ送りができるボタンを設置します。

画像下にテキスト付きで設置されてるパターンもありますが、今回は画像横にアイコン的なボタンを設置します。

ボタンを配置

<div class="arow-wrap">
    <div class="arrow-left">
      <button class="arrow-btn js-btn-back" type="button"></button>
    </div>
    <div class="arrow-right">
      <button class="arrow-btn js-btn-next" type="button"></button>
    </div>
</div>

HTMLはこんな感じで左右に設置する土台を作ります。js-がついているクラスはjQueryで読み込む用です。

ボタンは表示枠内、左右端のあたりに設置します。

/* ボタンタグの装飾リセット */
button {
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  vertical-align: middle;
  color: inherit;
  font: inherit;
  border: 0;
  background: transparent;
  padding: 0;
  margin: 0;
  outline: none;
  border-radius: 0;
}
/*********** スライド送りボタン ***********/
/* 共有パーツ */
.arow-wrap {
  width: 90%;
  height: 100%;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.arrow-btn {
  width: 48px;
  height: 48px;
  background-color: rgba(31, 84, 145, 0.804);
  border-radius: 50%;
  transition: .2s;
}
.arrow-btn:focus {
  box-shadow: 0px 1px 10px -2px rgba(0, 0, 0, 0.8);
}
.arrow-btn:hover {
  background-color: rgb(51, 79, 216);
  box-shadow: 0px 1px 10px -2px rgba(0, 0, 0, 0.8);
}

まずはbuttonタグの装飾をリセットさせました。

次にボタンの表示領域は表示枠のcarouselに対して90%にしてdisplay:flexで横並びにし、上下真ん中の左右端に寄せています。

.arrow-btnは青丸の部分です。focus時とhover時の装飾もここでしています。

ボタンの中身(デザイン)

配置をしたら次は中身です。

/* 左 */
.arrow-left {
  position: relative
}
/* ボタン左の中身(三角を擬似要素で表現) */
.arrow-left:before {
  content: "";
  width: 10px;
  height: 10px;
  border-top: 2px solid #fefefe;
  border-left: 2px solid #fefefe;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-30%, -50%)rotate(-45deg);
}
/* 右 */
.arrow-right {
  position: relative
}
/* ボタン右の中身(三角を擬似要素で表現) */
.arrow-right:before {
  content: "";
  width: 10px;
  height: 10px;
  border-top: 2px solid #fefefe;
  border-left: 2px solid #fefefe;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-70%, -50%)rotate(135deg);
}

中身の三角は、左右のボタンクラスの擬似要素で作っています。これで下画像の状態になったかと思います。

スライド送りボタン設置後

jQueryで必要要素をCSSメソッドでDOM操作

ここでは載せませんが、jQueryを使うのでCDNコードはHTMLのbodyの閉じタグ前に読み込んでおきましょう!詳しくはこちらから↓

>> 【最新版】jQueryのCDNを読み込む方法【超簡単です!】

関数作成の前に、必要な変数の定義からしていきます。

$(function () {
  //////////  スライドリストの合計幅を計算 → CSSでエリアに代入
  let width = $('.carousel-list').outerWidth(true); // .carousel-listの1枚分の幅
  let length = $('.carousel-list').length; // .carousel-listの数
  let slideArea = width * length; // レール全体幅 = スライド1枚の幅 × スライド合計数
  $('.carousel-area').css('width', slideArea); // カルーセルレールに計算した合計幅を指定
変数の内容
  • width  ・・・スライド1枚分の幅
  • length  ・・・スライドの数
  • slideArea・・・全スライドの幅合計(スライドレールの幅)

これらの値は実数のままでもいいですが、スライドが増えた時などまとめておくと保守の観点からも楽です。

そしてcss()メソッドでDOM操作しているslideAreaの値は、先ほど計算して記述しているのでスタイルシートから削除しておきます。

※忘れた方はこちらから > carousel-areaの全体幅計算(スクロールします)

.carousel-area {
  /* width: 3150px; (削除していいやつ)*/
  height: 100%;
  display: flex;
}
@media screen and (max-width: 600px) {
  .carousel-area {
    /* width: 1350px; (削除していいやつ) */
  }
}

そうするとこんな感じで表示枠(.carousel)内にすべてスライドが収まってしまいます。

DOMでスライド全体幅指定直後

この理由はcarouselに指定されてる幅に対して、carousel-areaに指定されてるdisplay: flexが先に効いてしまっていることが原因です。

なのでまず.carouselにposition: relativeを指定して、.carousel-areaにposition: absoluteを指定すれば意図した配置になります。

.carousel {
  width: 600px;
  height: calc(600px * 0.5625);
  margin: 0 auto;
  position: relative; /* ←追記 */
}
.carousel-area {
  height: 100%;
  display: flex;
  position: absolute; /* ←追記 */
}

スライド番号、スライド番号を変数化

先ほどの変数の下に続きを書いていきます。まずは下記変数を追加しています。

//////////  スライド現在値と最終スライド
  let slideCurrent = 0; // スライド現在値(1枚目のスライド番号としての意味も含む)
  let lastCurrent = $('.carousel-list').length - 1; // スライドの合計数=最後のスライド番号
変数の内容
  • slideCurrent ・・・スライド初期値(現在値)
  • lastCurrent ・・・スライドの合計数

これはスライドのインデックス番号を表しています。インデックスは0から始まるので1枚目(slideCurrent)は0としています。

なお、後からインデックス番号を足したり引いたりしますが、その番号はslideCurrentに代入していくので「初期値」かつ「現在値」と表現しておきます。

また、lastCurrentはスライド合計枚数=最終スライド番号として集計しています。

あと.carousel-listの数を.lengthで集計していますが、今の状態だと「5」が返ります。ただ、インデックス番号的には0から数えると最終スライドは「4」が返ります。

なのでlastCurrentは-1をして、最終スライド番号はインデックス番号の「4」となるようにしています。

スライドの動きを関数化

次にスライドの動きを関数でまとめます。

////////// スライドの切り替わりを「changeslide」として定義
 function changeslide() {
   $('.carousel-area').stop().animate({ // stopメソッドを入れることでアニメーション1回毎に止める
     left: slideCurrent * -width // 代入されたスライド数 × リスト1枚分の幅を左に動かす
   });
  };

関数名は「changeslide」にして、中身はanimate関数でcarousel-area(スライドレール全体)を左にズラすという動きにしました。

一行にするとこんな感じ。

 $('.carousel-area').stop().animate({ left: slideCurrent * -width });

left(左)にズラす幅は、変数「slideCurrent」× 変数「width」です。要するにスライド枚数 × スライド(.carousel-list)1枚分の幅ということになります。※左移動なのでwidthはマイナス指定です。

また、コメントアウトにも記載してますが、ボタンクリック時のアニメーションを1回毎に止めるためにstopメソッドを入れています。

jQueryは作動中のアニメが完了しないと次のアニメが再生されないので、ボタン連打した時なんかは連打した回数分だけ消化仕切れなかったアニメーションが発動してしまい、ちょっと分かりづらいですが下の動画のようになります。

※[動画]stopメソッドが無い場合

これを止めるために.stop()を入れています。

ボタンクリックで関数呼び出し

次にボタンクリック時の動作です。

//////////// ボタンクリック時「changeslide」関数を呼び出し
// NEXTボタン
  $('.js-btn-next').click(function () {
    if (slideCurrent === lastCurrent) { // 現在のスライドが最終スライドの場合
      slideCurrent = 0;
      changeslide(); // スライド初期値の値を代入して関数実行(初めのスライドに戻す)
    } else {
      slideCurrent++;
      changeslide(); // そうでなければスライド番号を増やして(次のスライドに切り替え)関数実行
    };
  });
 // BACKボタン
  $('.js-btn-back').click(function () {
    if (slideCurrent === 0) { // 現在のスライドが初期スライドの場合
      slideCurrent = lastCurrent;
      changeslide(); // 最終スライド番号を代入して関数実行(最後のスライドに移動)
    } else {
      slideCurrent--;
      changeslide(); // そうでなければスライド番号を減らして(前のスライドに切り替え)関数実行
    };
  });

$(‘.js-btn-next‘).clickと、$(‘.js-btn-back‘).click、それぞれ左右のボタンに対してクリックイベントを設定しています。

右側のNEXTボタン押下時は、現在のスライドが最終スライドの場合、スライド初期値の値を代入して関数実行(初めのスライドに戻す)、そうでなければスライド番号を増やして(次のスライドに切り替え)関数実行

左側のBACKボタン押下時は、現在のスライドが初期スライドの場合、最終スライド番号を代入して関数実行(最後のスライドに移動)、そうでなければスライド番号を減らして(前のスライドに切り替え)関数実行、という式です。

これまで設定した変数を上記日本語の通りにif式にあてているだけなので、そこまで難しい仕組みではありませんね。

ここまでで、下記のような状態になっているはずです。

ここまでで一応スライダー機能自体は完成しました。ここからは少しだけカスタマイズしていきます。

ページネーションを設定

ページネーションとは、スライド枚数が何枚あり、現在何枚目のスライドなのかを表示させるやつです。

ページネーションの配置とデザイン

<div class="pagination">
    <span class="pagination-circle target"></span>
    <span class="pagination-circle"></span>
    <span class="pagination-circle"></span>
    <span class="pagination-circle"></span>
    <span class="pagination-circle"></span>
</div>

今回はspanタグで印を作ります。スライド数にあわせて用意し、表示位置調整のためにdivで囲っています。

一つ目のspanタグに「target」というクラスをつけていますが、これは後ほど説明します。

.pagination {
  width: 150px;
  margin: 5% auto 0;
  display: flex;
  justify-content: space-around;
}
.pagination-circle {
  width: 20px;
  height: 20px;
  border: 1px solid #333;
  border-radius: 50%;
  background-color: rgba(83, 97, 223, 0.3);
}
/* jsでtargetクラスがついたら背景色を変える */
.pagination-circle.target {
  background-color: rgba(83, 97, 223, 0.8);
}

次にCSSですが、親要素paginationクラスをFlexboxのjustify-content: space-aroundで良い感じに横並びに配置させます。

pagination-circleが肝心の印部分ですが、.targetがつくクラスだけ色に変化を加えています。現在だと1枚目のスライドにつけています。

ここまでで、下のような感じになっているかと思います。

ページネーションを付けた状態

jQueryでページネーションを管理

次にjQueryです。先ほど定義したchangeslide関数の中に追加します。

function changeslide() {
    $('.carousel-area').stop().animate({
      left: slideCurrent * -width
    });
  ////////// ページネーションの変数を定義(=スライド現在値が必要)
  let pagiNation = slideCurrent + 1; // nth-of-typeで指定するため0に+1をする
  $('.pagination-circle').removeClass('target'); // targetクラスを削除
  $(".pagination-circle:nth-of-type(" + pagiNation + ")").addClass('target'); // 現在のボタンにtargetクラスを追加
};

内容としては、現在表示されているスライドが何枚目なのかを取得して、pagination-circleクラスにtargetクラスを付与するというものです。

表示されているスライドが何枚目なのかは、先ほど記述したクリックイベントの中で、変数「slideCurrent」に代入させていましたのでその時の値を使います。

忘れた方はこちらから >クリックイベントと変数slideCurrentについて(スクロールします)

ここではCSSでよく使われるnth-of-type()を用いて、nth-of-type()目のpagination-circleにtargetクラスを付与、という式にしています。

slideCurrentはインデックス番号を代入しているので0からスタートしてますが、nth-of-typeは1からカウントします。

なので、slideCurrent+1の数字をpagiNationという新しい変数に入れています。

あと、removeClass(‘target’)でtargetクラスを毎回削除することも忘れないように。

タイマーで自動スライド送り設定

次に時間毎にスライドが動く仕組みを作ります。

let Timer;  // 変数Timer
////////// 一定時間毎に処理実行する「startTimer」として関数を定義
function startTimer() {
  // 変数Timerに下記関数内容を代入する
  Timer = setInterval(function () { // setInterval・・・指定した時間ごとに関数を実行
    if (slideCurrent === lastCurrent) {
      slideCurrent = 0;
      changeslide();
    } else {
      slideCurrent++;
      changeslide();
    };
  }, 3000); // 上記動作を3秒毎に
}

理由はすぐ後で説明しますが、今回記述する内容はTimerという変数に格納しておきます。中身はif式にして、ボタンのクリックイベントで記述したものと同じです。

そして、タイマー設定に必要なsetIntervalです。これは指定した時間ごとに繰り返し関数を実行するものです。

setInterval (function () { 〜〜 }, 3000);

この記述だと3秒毎に〜するという意味になります。なので「startTimer」関数の中身は、3秒毎にchangeslide関数を実行するというものになりました。

あとはこの関数を呼び出します。

//////// 自動スライド切り替えタイマーを発動
startTimer();

これで自動でスライドが動くようになったのですが、このままだと一つ問題があります。

例えば1枚目から2枚目へ2.9秒時点でボタンクリックでスライド送りした場合、2枚目は0.1秒で即3枚目に移動していまいます。

ボタンクリック時は都度秒数カウントをリセットしたいので、ボタンクリック時用としてタイマーを止める関数も用意します。

////////// 「startTimer」関数を止める「stopTimer」関数を定義
  function stopTimer() {
    clearInterval(Timer); // clearInterval・・・setIntervalで設定したタイマーを取り消す
  }

clearIntervalsetInterval()でセットしたタイマーを解除するものです。

今回は「stopTimer」という名前で関数を定義し、中身は先ほど用意したsetIntervalメソッドが代入されている変数Timerを止める、ということにしています。

こういったところでコードが長くなるので、setIntervalの内容は変数(Timer)に入れておいた、という訳です。

ボタンクリック時もタイマーを正常作動させる

ボタンクリック時の関数にstartTimer関数とstopTimer関数を追記します。

// NEXTボタン
  $('.js-btn-next').click(function () {
    // 動いているタイマーをストップして再度タイマーを動かし直す(こうしないとページ送り後の秒間隔がズレる)
    stopTimer();
    startTimer();
    if (slideCurrent === lastCurrent) {
      slideCurrent = 0;
      changeslide();
    } else {
      slideCurrent++;
      changeslide();
    };
  });
  // BACKボタン
  $('.js-btn-back').click(function () {
    // 動いているタイマーをストップして再度タイマーを動かし直す(こうしないとページ送り後の時間間隔がズレる)
    stopTimer();
    startTimer();
    if (slideCurrent === 0) {
      slideCurrent = lastCurrent;
      changeslide();
    } else {
      slideCurrent--;
      changeslide();
    };
  });

これで「タイマーリセット→カウント開始」のサイクルがボタンクリック毎に行われます。

コードまとめ

最後にコードをまとめて載せておきます。デモタイトルの記述など説明省いている部分も追加しています。

かなり長いので不要の方は飛ばしちゃってください。 >このセクションは飛ばす(スクロールします)

<body>
  <h1 class="demo-title">カルーセル デモサイト</h1>
  <div class="carousel">
    <ul class="carousel-area">
      <!-- 意味的に画像はimgタグで配置してるが、CSSで非表示にする。 -->
      <li class="carousel-list"><img class="carousel-img" src="img/hamster.jpg" alt="ハムスターの画像"></li>
      <li class="carousel-list"><img class="carousel-img" src="img/sheep.jpg" alt="羊の画像"></li>
      <li class="carousel-list"><img class="carousel-img" src="img/turtle.jpg" alt="亀の画像"></li>
      <li class="carousel-list"><img class="carousel-img" src="img/bird.jpg" alt="鳥の画像"></li>
      <li class="carousel-list"><img class="carousel-img" src="img/lion.jpg" alt="ライオンの画像"></li>
    </ul>
    <div class="arow-wrap">
      <div class="arrow-left">
        <button class="arrow-btn js-btn-back" type="button"></button>
      </div>
      <div class="arrow-right">
        <button class="arrow-btn js-btn-next" type="button"></button>
      </div>
    </div>
    <div class="pagination">
      <span class="pagination-circle target"></span>
      <span class="pagination-circle"></span>
      <span class="pagination-circle"></span>
      <span class="pagination-circle"></span>
      <span class="pagination-circle"></span>
    </div>
  </div>
  <script
  src="https://code.jquery.com/jquery-3.6.0.min.js"
  integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
  crossorigin="anonymous"></script>
  <script type="text/javascript" src="script.js"></script>
</body>
/* ベースCSS・リセットCSS */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  list-style: none;
}
button {
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  vertical-align: middle;
  color: inherit;
  font: inherit;
  border: 0;
  background: transparent;
  padding: 0;
  margin: 0;
  outline: none;
  border-radius: 0;
}
body {
  overflow: hidden;
}
.demo-title {
  font-size: 24px;
  margin-bottom: 1em;
  padding: 1em 0;
  text-align: center;
  color: #fefefe;
  background-color: #1f5491;
}
/*********** ここまでベースCSS・リセットCSS ***********/
.carousel {
  width: 600px;
  height: calc(600px * 0.5625);
  position: relative;
  margin: 0 auto;
}
.carousel-area {
  /* リスト数×リスト幅を計算してwidth指定してもいいが、汎用性を考慮してjQueryで計算代入する */
  height: 100%;
  position: absolute;
  display: flex;
}
/* object-fitを使用せずに画像トリミングさせるためにbackgroundで指定 */
.carousel-list {
  width: 600px;
  height: 100%;
  margin-right: 30px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
@media screen and (max-width: 600px) {
  .carousel {
    width: 300px;
    height: calc(300px * 0.5625);
  }
  .carousel-list {
    width: 300px;
    height: calc(300px * 0.5625);
    margin-right: 0;
  }
}
.carousel-list:nth-child(1) {
  background-image: url(./img/hamster.jpg);
}
.carousel-list:nth-child(2) {
  background-image: url(./img/sheep.jpg);
}
.carousel-list:nth-child(3) {
  background-image: url(./img/turtle.jpg);
}
.carousel-list:nth-child(4) {
  background-image: url(./img/bird.jpg);
}
.carousel-list:nth-child(5) {
  background-image: url(./img/lion.jpg);
}
/* clipで非表示指定(スクリーンリーダー対策) */
.carousel-img {
  width: 1px;
  height: 1px;
  clip: rect(1px, 1px, 1px, 1px);
  -webkit-clip-path: inset(50%);
  clip-path: inset(50%);
  margin: -1px;
  padding: 0;
  overflow: hidden;
  position: absolute;
}
/*********** スライド送りボタン ***********/
/* 共有パーツ */
.arow-wrap {
  width: 90%;
  height: 100%;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.arrow-btn {
  width: 48px;
  height: 48px;
  background-color: rgba(31, 84, 145, 0.804);
  border-radius: 50%;
  transition: .2s;
}
.arrow-btn:focus {
  box-shadow: 0px 1px 10px -2px rgba(0, 0, 0, 0.8);
}
.arrow-btn:hover {
  background-color: rgb(51, 79, 216);
  box-shadow: 0px 1px 10px -2px rgba(0, 0, 0, 0.8);
}
/* 左 */
.arrow-left {
  position: relative
}
.arrow-left:before {
  content: "";
  width: 10px;
  height: 10px;
  border-top: 2px solid #fefefe;
  border-left: 2px solid #fefefe;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-30%, -50%)rotate(-45deg);
}
/* 右 */
.arrow-right {
  position: relative
}
.arrow-right:before {
  content: "";
  width: 10px;
  height: 10px;
  border-top: 2px solid #fefefe;
  border-left: 2px solid #fefefe;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-70%, -50%)rotate(135deg);
}
@media screen and (max-width: 600px) {
  .arrow-btn {
    width: 30px;
    height: 30px;
  }
}
/*********** ページネーション ***********/
.pagination {
  width: 150px;
  margin: 5% auto 0;
  display: flex;
  justify-content: space-around;
}
.pagination-circle {
  width: 20px;
  height: 20px;
  border: 1px solid #333;
  border-radius: 50%;
  background-color: rgba(31, 84, 145, 0.471);
}
/* jsでtargetクラスがついたら背景色を変える */
.pagination-circle.target {
  background-color: rgba(31, 84, 145, 0.804);
}
$(function () {
  //////////////////////////// 【必要な変数を定義】////////////////////////////
  //////////  スライドリストの合計幅を計算→CSSでエリアに代入
  let width = $('.carousel-list').outerWidth(true); // .carousel-listの1枚分の幅
  let length = $('.carousel-list').length; // .carousel-listの数
  let slideArea = width * length; // レール全体幅 = スライド1枚の幅 × スライド合計数
  $('.carousel-area').css('width', slideArea); // カルーセルレールに計算した合計幅を指定
  //////////  スライド現在値と最終スライド
  let slideCurrent = 0; // スライド現在値(1枚目のスライド番号としての意味も含む)
  let lastCurrent = $('.carousel-list').length - 1; // スライドの合計数=最後のスライド番号
  ////////////////////////////【スライドの動き方+ページネーションに関する関数定義】////////////////////////////
  ////////// スライドの切り替わりを「changeslide」として定義
  function changeslide() {
    $('.carousel-area').stop().animate({ // stopメソッドを入れることでアニメーション1回毎に止める
      left: slideCurrent * -width // 代入されたスライド数 × リスト1枚分の幅を左に動かす
    });
    ////////// ページネーションの変数を定義(=スライド現在値が必要)
    let pagiNation = slideCurrent + 1; // nth-of-typeで指定するため0に+1をする
    $('.pagination-circle').removeClass('target'); // targetクラスを削除
    $(".pagination-circle:nth-of-type(" + pagiNation + ")").addClass('target'); // 現在のボタンにtargetクラスを追加
  };
  /////////////////////////【自動スライド切り替えのタイマー関数定義】/////////////////////////
  let Timer;
  ////////// 一定時間毎に処理実行する「startTimer」として関数を定義
  function startTimer() {
    // 変数Timerに下記関数内容を代入する
    Timer = setInterval(function () { // setInterval・・・指定した時間ごとに関数を実行
      if (slideCurrent === lastCurrent) { // 現在のスライドが最終スライドの場合
        slideCurrent = 0;
        changeslide(); // スライド初期値の値を代入して関数実行(初めのスライドに戻す)
      } else {
        slideCurrent++;
        changeslide(); // そうでなければスライド番号を増やして(次のスライドに切り替え)関数実行
      };
    }, 3000); // 上記動作を3秒毎に
  }
  ////////// 「startTimer」関数を止める「stopTimer」関数を定義
  function stopTimer() {
    clearInterval(Timer); // clearInterval・・・setIntervalで設定したタイマーを取り消す
  }
  //////// 自動スライド切り替えタイマーを発動
  startTimer();
  /////////////////////////【ボタンクリック時関数を呼び出し】/////////////////////////
  // NEXTボタン
  $('.js-btn-next').click(function () {
    // 動いているタイマーをストップして再度タイマーを動かし直す(こうしないとページ送り後の秒間隔がズレる)
    stopTimer();
    startTimer();
    if (slideCurrent === lastCurrent) { // 現在のスライドが最終スライドの場合
      slideCurrent = 0;
      changeslide(); // スライド初期値の値を代入して関数実行(初めのスライドに戻す)
    } else {
      slideCurrent++;
      changeslide(); // そうでなければスライド番号を増やして(次のスライドに切り替え)関数実行
    };
  });
  // BACKボタン
  $('.js-btn-back').click(function () {
    // 動いているタイマーをストップして再度タイマーを動かし直す(こうしないとページ送り後の時間間隔がズレる)
    stopTimer();
    startTimer();
    if (slideCurrent === 0) { // 現在のスライドが初期スライドの場合
      slideCurrent = lastCurrent;
      changeslide(); // 最終スライド番号を代入して関数実行(最後のスライドに移動)
    } else {
      slideCurrent--;
      changeslide(); // そうでなければスライド番号を減らして(前のスライドに切り替え)関数実行
    };
  });
});

最後に

シンプルな仕様の割りに今回はかなり長くなってしまいました。

スライダーは他にも、無限ループやサムネイルに連動した画像切り替え、タッチデバイスのスワイプに反応など多くの仕様があります。

今回のまとめで全て導入してみようとも思ったんですが、あまりにも長くなるのと(疲れるのと)、そこまで仕様テンコ盛りならプラグインを使用した方が遥かに効率的だったりするので、実装する場合は事前に考えて取り組んでみてください!

また、他にも汎用的なUIを当記事にまとめているのでチェックしてくれると嬉しいです!


また、当記事を読まれている方の中にはWeb制作初学者の方もいるかと思います。デザインやコーディングの基礎知識を学びたい方向けの記事を用意しているので是非見ていってください!

PENGIN無料コーディング課題

オススメ書籍紹介

オススメUdemy講座紹介