【jQuery】スライドショー・カルーセルをjQueryで実装する

JavaScript

こんにちは、かつコーチです。

前回はモーダルウィンドウをjQueryで実装しました。

今回は、トップページなどでよく見るスライダー(カルーセル)をjQueryで実装していきます。

素のJavaScript版ではCSSの transform: translateX() を使いましたが、jQueryでは animate() メソッドを使うと、CSSのtransitionを書かなくてもアニメーションを実現できます。

「保守案件でjQueryの animate() が出てきたけど、CSSのtransitionと何が違うのか分からない」という方は、ぜひこの記事で違いを整理してください。

jQueryのanimate()メソッドとは

CSSのtransitionとの違い

animate()とは、CSSの数値プロパティ(left、top、widthなど)を指定した時間をかけて変化させるjQueryのメソッドです。

素のJavaScript版では、CSSに transition: transform 0.4s ease; を書いておき、JavaScript側は style.transform の値を変更するだけでした。

jQueryの animate() はこれとは逆に、アニメーション自体をJavaScript側で計算しながら実行するという違いがあります。

方法アニメーションの制御場所対応できるプロパティ
CSSのtransition + JSでスタイル変更CSS側transformを含む多くのプロパティ
jQueryのanimate()JavaScript側lefttopwidthopacityなど数値プロパティのみ

animate()transform を直接アニメーションさせることができないため、スライダーの横移動には left プロパティを使うのが定番です。

実装方針を先に整理する

素のJS版と同じく、状態として管理するのは次の3つです。

  • 現在のスライド番号currentIndex
  • スライドの総数slideCount
  • 自動再生のタイマーID(止めるときに必要)

考え方自体は素のJavaScript版と共通なので、「変わるのは状態を画面に反映する部分の書き方だけ」と捉えると理解しやすくなります。

基本の実装手順

手順1:HTMLとCSSを用意する

<div class="slider">
  <div class="slider__track" id="sliderTrack">
    <div class="slider__slide"><img src="slide1.jpg" alt="スライド1"></div>
    <div class="slider__slide"><img src="slide2.jpg" alt="スライド2"></div>
    <div class="slider__slide"><img src="slide3.jpg" alt="スライド3"></div>
  </div>
  <button class="slider__btn slider__btn--prev" id="prevBtn" aria-label="前へ">‹</button>
  <button class="slider__btn slider__btn--next" id="nextBtn" aria-label="次へ">›</button>
  <div class="slider__dots" id="sliderDots"></div>
</div>
.slider {
  position: relative;
  overflow: hidden;
  max-width: 600px;
}

.slider__track {
  display: flex;
  position: relative; /* leftプロパティで動かすためrelativeにしておく */
}

.slider__slide {
  min-width: 100%;
}

animate()left を動かすため、CSS側の transition の指定は不要になります。

手順2:jQueryで切り替えロジックを実装する

$(function () {
  const $track = $('#sliderTrack');
  const $slides = $track.children();
  const slideCount = $slides.length;
  const $dotsContainer = $('#sliderDots');

  let currentIndex = 0;

  // インジケーターをスライド枚数分だけ動的に生成する
  for (let i = 0; i < slideCount; i++) {
    const $dot = $('<button>').addClass('slider__dot');
    if (i === 0) $dot.addClass('is-active');
    $dot.on('click', function () {
      goToSlide(i);
    });
    $dotsContainer.append($dot);
  }
  const $dots = $dotsContainer.children();

  function goToSlide(index) {
    currentIndex = index;

    $track.animate(
      { left: `-${currentIndex * 100}%` },
      400 // 400ミリ秒かけて移動する
    );

    $dots.removeClass('is-active').eq(currentIndex).addClass('is-active');
  }

  function nextSlide() {
    const nextIndex = (currentIndex + 1) % slideCount;
    goToSlide(nextIndex);
  }

  function prevSlide() {
    const prevIndex = (currentIndex - 1 + slideCount) % slideCount;
    goToSlide(prevIndex);
  }

  $('#nextBtn').on('click', nextSlide);
  $('#prevBtn').on('click', prevSlide);
});

$('<button>') のように、タグ文字列を $() に渡すと新しい要素を生成できます。

素のJS版の document.createElement() に対応する書き方です。

.eq(currentIndex) は「何番目の要素か」をインデックスで指定するjQuery特有のメソッドで、removeClass().eq(index).addClass() とメソッドチェーンでつなげられるのが便利なポイントです。

剰余演算子(%)を使った循環の考え方は、素のJS版とまったく同じです。

よくあるつまずきポイント・エラー対処

Before/After:animateを連打すると動きがガクガクになる

私が最初にjQueryで自動再生スライダーを作ったとき、ボタンを連打すると、スライドがガクガクと不自然に前後に動く現象に遭遇しました。

❌ Before:連打対策をしていない

$('#nextBtn').on('click', function () {
  nextSlide();
});
// animateが完了する前に次のanimateが呼ばれると、アニメーションのキューが積み重なる

animate() は呼び出すたびに「アニメーションのキュー(順番待ちの列)」に処理を追加します。

ボタンを連打すると、前のアニメーションが終わっていないのに次のアニメーションが予約され、後から「溜まった分のアニメーションが順番に再生される」という状態になり、見た目がガクガクした動きになります。

✅ After:stop()でキューをクリアしてから動かす

function goToSlide(index) {
  currentIndex = index;

  $track.stop(true, true).animate(
    { left: `-${currentIndex * 100}%` },
    400
  );

  $dots.removeClass('is-active').eq(currentIndex).addClass('is-active');
}

stop(true, true) は「今実行中のアニメーションを止め、溜まっているキューもすべて破棄したうえで、現在のアニメーションを最終状態まで一気に進める」という指定です。

animate() を使うUIでは、この stop() によるキュー対策がほぼ必須のテクニックだと覚えておいてください。

Before/After:自動再生タイマーの多重起動

素のJS版でも紹介されていた「タイマーが多重に動いてしまう」問題は、jQueryでも同じように起こります。

❌ Before:タイマーを止めずに何度も再生成してしまう

let timerId;

function startAutoPlay() {
  timerId = setInterval(nextSlide, 3000);
}

$('.slider').on('mouseleave', function () {
  startAutoPlay(); // 呼ばれるたびに新しいタイマーが増えていく
});

✅ After:開始前に必ず既存のタイマーを止める

let timerId = null;

function startAutoPlay() {
  stopAutoPlay();
  timerId = setInterval(nextSlide, 3000);
}

function stopAutoPlay() {
  clearInterval(timerId);
  timerId = null;
}

$('.slider').on('mouseenter', stopAutoPlay);
$('.slider').on('mouseleave', startAutoPlay);

startAutoPlay();

setInterval / clearInterval はDOM標準のAPIなので、jQueryを使っていても書き方は素のJavaScript版と変わりません。

jQuery特有の animate() の話と混同しないよう、「アニメーションのキュー管理はstop()」「タイマーの多重起動対策はclearInterval()」と分けて覚えておくと整理しやすくなります。

応用:easing(イージング)を変える

jQuery標準のeasingオプション

animate() の第3引数(または設定オブジェクトの easing プロパティ)で、動きの緩急を変更できます。

$track.stop(true, true).animate(
  { left: `-${currentIndex * 100}%` },
  {
    duration: 400,
    easing: 'swing', // jQuery標準では linear か swing のみ
  }
);

jQuery本体が標準で持っているイージングは linear(一定速度)と swing(滑らかな緩急)の2種類だけです。

「もっとバリエーションが欲しい」という場合は、jQuery UIライブラリを追加で読み込むことで easeInOutQuad のような多彩なイージングが使えるようになりますが、レガシー案件の保守であれば、まずは標準の2種類で十分なケースがほとんどです。

まとめ

この記事のポイント

  • jQueryのanimate()は、leftなどの数値プロパティをJavaScript側でアニメーションさせるメソッド
  • $('<button>')のようにタグ文字列を$()に渡すと新しい要素を生成できる
  • animate()を連打すると、アニメーションのキューが溜まってガクガクした動きになるためstop(true, true)で対策する
  • setIntervalによるタイマーの多重起動対策は、素のJavaScript版と同じ考え方で解決できる
  • animate()標準のeasingはlinearswingの2種類のみ

次に読むべき記事

次回は、フォームのバリデーションをjQueryで実装します。

submitイベントを使ったチェックと、エラーメッセージ表示の実装方法を解説します。

→ 次の記事:フォームのバリデーションをjQueryで実装する

タグ:jQuery, 中級者向け, UI実装

タイトルとURLをコピーしました