JavaScript02 応用編|配列なし → ボタン操作 → 自動スライド → 無限ループ → レスポンシブ対応まで学べる自作スライドショー

JavaScript スライドショー

JavaScriptでスライドショーを作る方法を学んだ前回(入門編)では、
配列で画像を管理し、一定時間ごとに切り替える基本的な動きを実装しました。

今回の「応用編」では、その仕組みをさらに発展させ、実際のWebサイトで使えるレベルのスライドショーを、段階的に作り上げていきます。ただの画像切り替えではなく、以下のような機能を「ゼロから自作」します。

  • 画面幅に応じて自動調整されるレスポンシブ対応
  • 配列を使わず、HTMLに書かれた画像をJavaScriptで操作する方法
  • 「次へ」「前へ」ボタンで手動操作できるスライドショー
  • ボタン操作を検出して、自動スライドを一時停止・再開する仕組み
  • 現在の画像を示すインジケーター(ドットナビ)機能
  • フェードではなく、横にスライドする「スライドイン効果」
  • 無限ループ風のスムーズなスライド(ダミー画像+transformの応用)
目次

JavaScript:配列を使わないスライドショー

02では、JavaScript内の配列で画像を指定した場合でしたが、画像部分はHTMLで準備したい・・・という要望がありましたので以下は、画像をHTMLで配置し、JavaScript では、img要素を取得して処理をする内容となります。CSS部分は02の内容と同じです。

See the Pen Untitled by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.

HTMLの解説

 <div class="slideshow">
    <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1" class="active">
    <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2">
    <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3">
  </div>

  <script src="script.js"></script>

画像を静的に記述します。img要素を3つ直接HTML内に配置し、それをJavaScriptで切り替えます。

JavaScript構成

// HTML内のすべての画像を取得
const slides = document.querySelectorAll('.slideshow img');

// 現在のスライドインデックスを初期化
let currentIndex = 0;

// 画像を切り替える関数
const changeImage = () => {
  slides[currentIndex].classList.remove('active'); // 現在の画像を非表示
  currentIndex = (currentIndex + 1) % slides.length; // 次の画像のインデックスを計算
  slides[currentIndex].classList.add('active'); // 次の画像を表示
};

// 3秒ごとに画像を切り替える
setInterval(() => changeImage(), 3000);

  • document.querySelectorAll('.slideshow img')で、すべての画像要素を取得。
  • changeImage関数内で、現在の画像からactiveクラスを外し、次の画像にactiveクラスを付与。
  • setIntervalで3秒ごとにchangeImageを実行し、画像を切り替え。

JavaScript:「次へ」「前へ」ボタンを追加

HTMLに直接画像を記述する方法のまま、「次へ」「前へ」ボタンを追加していきます。まずは手動で、次へ、前へをクリックして画像が変わるようにします。

See the Pen 前へ次へ スライドショーjs by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.

HTML

  <div class="slideshow">
    <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1" class="active">
    <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2">
    <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3">
    <button class="prev">前へ</button>
    <button class="next">次へ</button>
  </div>

  <script src="script.js"></script>

CSS

.slideshow {
  width: 800px;
  height: 400px;
  overflow: hidden;
  position: relative;
}

.slideshow img {
  width: 800px;
  height: 400px;
  object-fit: cover;
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  transition: opacity 1s ease-in-out; /* フェードアニメーション */
}

.slideshow img.active {
  opacity: 1; /* 表示する画像の透明度を変更 */
}

button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  border: none;
  padding: 10px 20px;
  cursor: pointer;
  font-size: 16px;
}

button.prev {
  left: 10px; /* 左端に配置 */
}

button.next {
  right: 10px; /* 右端に配置 */
}

JavaScriptの解説

// スライドショーの要素を取得
const slides = document.querySelectorAll('.slideshow img');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');

// 現在のスライドインデックス
let currentIndex = 0;

// 画像を切り替える関数
const showSlide = (index) => {
  slides.forEach((slide, i) => {
    slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与
  });
};

// 「次へ」ボタンのクリックイベント
nextButton.addEventListener('click', () => {
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex);
});

// 「前へ」ボタンのクリックイベント
prevButton.addEventListener('click', () => {
  currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動
  showSlide(currentIndex);
});

  • document.querySelectorAll('.slideshow img'): HTML内のすべての画像を取得。
  • showSlide(index):指定されたインデックスの画像にactiveクラスを付与し、他の画像からactiveクラスを削除。

イベントリスナー

addEventListenerは、特定のイベント(クリック、マウスオーバーなど)を監視し、そのイベントが発生したときに実行する処理(コールバック関数)を指定するメソッドです。

// 「次へ」ボタンのクリックイベント
nextButton.addEventListener('click', () => {
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex);
});
  1. nextButton.addEventListener('click', ...):
    • ボタン(nextButton)にクリックイベント('click')を登録。
    • ボタンがクリックされると、登録された関数(以下のアロー関数)が実行されます。
  2. currentIndex = (currentIndex + 1) % slides.length:
    • currentIndexは現在表示中のスライドを指すインデックスです。
    • currentIndex + 1: 次のスライドに進むためにインデックスを1つ増加させます。
    • % slides.length: スライドの総数(slides.length)で割った余りを計算。
      • これにより、最後のスライド(インデックスが最大値)から次に進むと、最初のスライド(インデックス0)に戻る動きが実現します。
  3. showSlide(currentIndex):
    • 現在のスライドを表示するための関数を呼び出し、スライドを切り替えます。
// 「前へ」ボタンのクリックイベント
prevButton.addEventListener('click', () => {
  currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動
  showSlide(currentIndex);
});
  1. prevButton.addEventListener('click', ...):
    • ボタン(nextButton)にクリックイベント('click')を登録。
    • ボタンがクリックされると、登録された関数(以下のアロー関数)が実行されます。
  2. currentIndex = (currentIndex - 1 + slides.length) % slides.length:
    • currentIndex - 1: 前のスライドに移動するためにインデックスを1つ減少。
    • + slides.length:
      • インデックスが負の値になる場合を防ぐために、スライド総数(slides.length)を加算。
      • JavaScriptでは負のインデックスをサポートしていないため、こうすることで負の値を正しいインデックスに変換。
    • % slides.length:
      • インデックスがスライド総数の範囲(0からslides.length - 1)内に収まるように調整。
  3. showSlide(currentIndex):
    • 現在のスライドを表示するための関数を呼び出し、スライドを切り替えます。

現在のスライドを表示するshowSlide関数

const showSlide = (index) => {
  slides.forEach((slide, i) => {
    slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与
  });
};
  • slides.forEach((slide, i) => {...}):
    • スライドショー内のすべての画像要素をループ処理。
    • 現在のスライドにのみactiveクラスを付与。
  • slide.classList.toggle('active', i === index)
    • i === index: 現在のスライドかどうかを判定。
    • toggle関数を使い、trueの場合、activeクラスを付与。
    • toggle関数を使い、falseの場合、activeクラスを削除。

前へ次へのボタン プラス 最初からスライドしたい

ボタンを追加するJavaScriptでは、クリックイベントの学習でしたので、最初からスライドしません。ここでは、自動スライドページを表示すると、スライドショーが自動で動き始め、手動操作の優先「次へ」「前へ」ボタンをクリックすると、操作中は自動スライドが一時停止し、再び自動スライドが再開されるように追加してみましょう。HTML/CSSはそのまま使います。

See the Pen 前へ次へプラス自動スライド by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.

JavaScriptの解説

自動スライドの機能を追加します。

// スライドショーの要素を取得
const slides = document.querySelectorAll('.slideshow img');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');

// 現在のスライドインデックス
let currentIndex = 0;

// 画像を切り替える関数
const showSlide = (index) => {
  slides.forEach((slide, i) => {
    slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与
  });
};

// 自動スライドの間隔
const autoSlideInterval = 3000; // 3秒

// 自動スライドをスタート
let autoSlide = setInterval(() => {
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex);
}, autoSlideInterval);

// 「次へ」ボタンのクリックイベント
nextButton.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex);
  autoSlide = setInterval(() => { // 再度自動スライドを開始
    currentIndex = (currentIndex + 1) % slides.length;
    showSlide(currentIndex);
  }, autoSlideInterval);
});

// 「前へ」ボタンのクリックイベント
prevButton.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動
  showSlide(currentIndex);
  autoSlide = setInterval(() => { // 再度自動スライドを開始
    currentIndex = (currentIndex + 1) % slides.length;
    showSlide(currentIndex);
  }, autoSlideInterval);
});
  1. 自動スライドの追加
    • setIntervalで3秒ごとにcurrentIndexを増加し、スライドを切り替えています。
    • showSlide(currentIndex)で切り替えを実行。
  2. クリック操作の一時停止と再開
    • 「次へ」「前へ」ボタンをクリックすると、現在のsetIntervalclearIntervalで一時停止。
    • ボタン操作後に新たなsetIntervalを再設定し、自動スライドを再開します。
  3. スライドのループ処理
    • currentIndex = (currentIndex + 1) % slides.length: 最後のスライドから次へ移動すると最初に戻る。
    • currentIndex = (currentIndex - 1 + slides.length) % slides.length: 最初のスライドから前へ移動すると最後に戻る。

JavaScript:インジケーターを設置

次は、前へ・次へのボタン、自動再生に加え、インジケーターを追加したスライドショーになります。インジケーターは、画像の下部にある丸のボタンで、今何枚目の写真が表示されているかを明示したり、クリックすることで写真が移動したりする目安のボタンです。

See the Pen 前へ次へ・インジケーターjs by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.

HTMLの解説

  <div class="slideshow">
    <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1" class="active">
    <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2">
    <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3">
    <button class="prev">前へ</button>
    <button class="next">次へ</button>
    <div class="indicators"></div>
  </div>
  <script src="script.js"></script>

<div class=”indicators”></div> が追加されます。

CSSの解説

.slideshow {
  width: 800px;
  height: 400px;
  overflow: hidden;
  position: relative;
}

.slideshow img {
  width: 800px;
  height: 400px;
  object-fit: cover;
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  transition: opacity 1s ease-in-out; /* フェードアニメーション */
}

.slideshow img.active {
  opacity: 1; /* 表示する画像の透明度を変更 */
}

button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  border: none;
  padding: 10px 20px;
  cursor: pointer;
  font-size: 16px;
}

button.prev {
  left: 10px; /* 左端に配置 */
}

button.next {
  right: 10px; /* 右端に配置 */
}

.indicators {
  position: absolute;
  bottom: 15px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
}

.indicators .indicator {
  width: 15px;
  height: 15px;
  background-color: rgba(255, 255, 255, 0.5);
  border-radius: 50%;
  cursor: pointer;
}

.indicators .indicator.active {
  background-color: rgba(255, 255, 255, 1);
}

.indicators

  • スライドショーの下に配置されるインジケーター(丸い要素)の親要素。
  • 中央揃えで横一列に並べられています。
  • 各インジケーターの間隔は10pxです。

.indicator

  • 各スライドを表す丸い要素。
  • サイズは15pxで、半透明の白い背景。
  • 丸い形状(border-radius: 50%)で、クリック可能に設定しています。

.indicator.active

現在表示中のスライドに対応するインジケーターを完全な白色で強調表示します。

JavaScript構成

// スライドショーの要素を取得
const slides = document.querySelectorAll('.slideshow img');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
const indicatorsContainer = document.querySelector('.indicators');

// 現在のスライドインデックス
let currentIndex = 0;

// 画像を切り替える関数
const showSlide = (index) => {
  slides.forEach((slide, i) => {
    slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与
  });
  updateIndicators(index); // インジケーターを更新
};

// インジケーターを作成
slides.forEach((_, index) => {
  const indicator = document.createElement('div');
  indicator.classList.add('indicator');
  if (index === 0) indicator.classList.add('active'); // 最初のインジケーターにactiveクラス
  indicator.addEventListener('click', () => {
    clearInterval(autoSlide); // 自動スライドを一時停止
    currentIndex = index; // インジケーターのクリックでスライドを切り替え
    showSlide(currentIndex);
    restartAutoSlide(); // 自動スライドを再開
  });
  indicatorsContainer.appendChild(indicator);
});

// インジケーターを更新
const updateIndicators = (index) => {
  const indicators = document.querySelectorAll('.indicator');
  indicators.forEach((indicator, i) => {
    indicator.classList.toggle('active', i === index); // 現在のスライドに対応するインジケーターを強調
  });
};

// 自動スライドの間隔
const autoSlideInterval = 3000; // 3秒

// 自動スライドをスタート
let autoSlide = setInterval(() => {
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex);
}, autoSlideInterval);

// 自動スライドを再開する関数
const restartAutoSlide = () => {
  autoSlide = setInterval(() => {
    currentIndex = (currentIndex + 1) % slides.length;
    showSlide(currentIndex);
  }, autoSlideInterval);
};

// 「次へ」ボタンのクリックイベント
nextButton.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex);
  restartAutoSlide(); // 自動スライドを再開
});

// 「前へ」ボタンのクリックイベント
prevButton.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動
  showSlide(currentIndex);
  restartAutoSlide(); // 自動スライドを再開
});


インジケーターを動的に生成

slides.forEach((_, index) => {
  const indicator = document.createElement('div');
  indicator.classList.add('indicator');
  if (index === 0) indicator.classList.add('active'); // 最初のインジケーターにactiveクラス
  indicator.addEventListener('click', () => {
    clearInterval(autoSlide); // 自動スライドを一時停止
    currentIndex = index; // インジケーターのクリックでスライドを切り替え
    showSlide(currentIndex);
    restartAutoSlide(); // 自動スライドを再開
  });
  indicatorsContainer.appendChild(indicator);
});
  1. slides.forEach:
    • スライドショーの画像数(slidesの数)に基づいてインジケーターを生成します。
    • 画像が3枚ならインジケーターも3つ。
  2. document.createElement('div'):
    • <div>要素を動的に生成して、インジケーターとして使用。
  3. indicator.classList.add('indicator'):
    • 新しいインジケーターにindicatorクラスを付与。
    • CSSでスタイルを適用するためのクラス設定。
  4. if (index === 0):
    • 最初のインジケーターactiveクラスを付与し、初期表示を強調。
  5. indicator.addEventListener('click', () => {...}):
    • インジケーターがクリックされたときの処理を定義。
    • クリックされたインジケーターに対応するスライドを表示するため、次の処理を実行します。
      • clearInterval(autoSlide):
        • 自動スライドを一時停止。
      • currentIndex = index:
        • クリックされたインジケーターに対応するスライドのインデックスを設定。
      • showSlide(currentIndex):
        • 該当スライドを表示。
      • restartAutoSlide():
        • 再度、自動スライドを再開。
  6. indicatorsContainer.appendChild(indicator):
    • 生成したインジケーターを.indicatorsコンテナに追加。

インジケーターの更新

const updateIndicators = (index) => {
  const indicators = document.querySelectorAll('.indicator');
  indicators.forEach((indicator, i) => {
    indicator.classList.toggle('active', i === index); // 現在のスライドに対応するインジケーターを強調
  });
};

  1. querySelectorAll('.indicator'):
    • すべてのインジケーターを取得。
  2. indicators.forEach((indicator, i) => {...}):
    • 各インジケーターをループして処理。
  3. indicator.classList.toggle('active', i === index):
    • 現在のスライド(index)に対応するインジケーターにactiveクラスを付与。
    • 他のインジケーターからはactiveクラスを削除。

自動スライドとの連携

既存の自動スライド機能(setInterval)やボタンのイベントリスナーと組み合わせて、インジケーターが常に最新の状態になるように更新されています。

let autoSlide = setInterval(() => {
  currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動
  showSlide(currentIndex); // スライドを切り替え
}, autoSlideInterval);

showSlide(currentIndex)内でupdateIndicatorsが呼び出されるため、自動スライド中もインジケーターが同期して更新されます。

右から左へのスライドイン効果へ変更

ここからは、スライドの動きをフェードから「スライドイン」へ変更します。その際、「無限ループ風スライドショー」になるようにすることでスライドの動きがスムーズになります。

See the Pen 前へ次へインジケーター スライドイン by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.

HTMLの解説

<div class="slideshow">
  <div class="slides">
    <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> <!-- ダミー最後 -->
    <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1">
    <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2">
    <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3">
    <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1"> <!-- ダミー最初 -->
  </div>
  <button class="prev">前へ</button>
  <button class="next">次へ</button>
  <div class="indicators"></div>
</div>

スライドの最初と最後にダミー画像を追加。実際には「01 → 02 → 03 → ダミー01」となり、スライド全体がループしているように見えます。

CSSの解説

.slides {
  display: flex;
  transition: transform 1s ease-in-out;
  transform: translateX(-800px); /* 最初のスライドに移動 */
}

.slideshow {
  width: 800px;
  height: 400px;
  overflow: hidden;
  position: relative;
}

.slideshow img {
  width: 800px;
  height: 400px;
  object-fit: cover;
}

button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  border: none;
  padding: 10px 20px;
  cursor: pointer;
  font-size: 16px;
}

button.prev {
  left: 10px;
}

button.next {
  right: 10px;
}

.indicators {
  position: absolute;
  bottom: 15px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
}

.indicators .indicator {
  width: 15px;
  height: 15px;
  background-color: rgba(255, 255, 255, 0.5);
  border-radius: 50%;
  cursor: pointer;
}

.indicators .indicator.active {
  background-color: rgba(255, 255, 255, 1);
}

  • .slidestransform: translateX()を使用して、画像全体を左右に移動させる。
  • transition: transform 1s ease-in-out;でアニメーションをスムーズに。
  • .slidesの初期位置をダミー最初の次(実スライド最初)に設定。

JavaScriptの解説

const slides = document.querySelector('.slides');
const slideImages = document.querySelectorAll('.slides img');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
const indicatorsContainer = document.querySelector('.indicators');

let currentIndex = 1; // 最初のスライド(ダミーを考慮して2番目)
const totalSlides = slideImages.length;
const autoSlideInterval = 3000; // 3秒

// スライドを移動する関数
const moveSlide = (index) => {
  slides.style.transition = 'transform 1s ease-in-out'; // スムーズに動く
  slides.style.transform = `translateX(${-index * 800}px)`;
};

// インジケーターを更新する関数
const updateIndicators = (index) => {
  const indicators = document.querySelectorAll('.indicator');
  indicators.forEach((indicator, i) => {
    indicator.classList.toggle('active', i === index - 1); // ダミーを考慮して調整
  });
};

// 位置リセット関数(無限ループ風に見せる)
const resetPosition = () => {
  slides.style.transition = 'none'; // トランジションを無効化
  if (currentIndex === totalSlides - 1) {
    currentIndex = 1; // ダミー最初から実スライド最初に移動
    slides.style.transform = `translateX(${-currentIndex * 800}px)`;
  } else if (currentIndex === 0) {
    currentIndex = totalSlides - 2; // ダミー最後から実スライド最後に移動
    slides.style.transform = `translateX(${-currentIndex * 800}px)`;
  }
};

// 自動スライド
let autoSlide = setInterval(() => {
  currentIndex++;
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット
}, autoSlideInterval);

// 手動操作でスライドを再開
const restartAutoSlide = () => {
  clearInterval(autoSlide);
  autoSlide = setInterval(() => {
    currentIndex++;
    moveSlide(currentIndex);
    setTimeout(resetPosition, 1000);
  }, autoSlideInterval);
};

// 「次へ」ボタン
nextButton.addEventListener('click', () => {
  clearInterval(autoSlide);
  currentIndex++;
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000);
  restartAutoSlide();
});

// 「前へ」ボタン
prevButton.addEventListener('click', () => {
  clearInterval(autoSlide);
  currentIndex--;
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000);
  restartAutoSlide();
});

// インジケーター作成
for (let i = 0; i < totalSlides - 2; i++) {
  const indicator = document.createElement('div');
  indicator.classList.add('indicator');
  if (i === 0) indicator.classList.add('active');
  indicator.addEventListener('click', () => {
    clearInterval(autoSlide);
    currentIndex = i + 1; // ダミーを考慮
    moveSlide(currentIndex);
    setTimeout(resetPosition, 1000);
    restartAutoSlide();
  });
  indicatorsContainer.appendChild(indicator);
}

ダミースライドを考慮した移動(moveSlide 関数)

const moveSlide = (index) => {
  slides.style.transition = 'transform 1s ease-in-out'; // アニメーションを適用
  slides.style.transform = `translateX(${-index * 800}px)`; // 現在のスライド位置に応じて移動
};

  • slides.style.transition:
    • スライド移動時のアニメーション(トランジション)を設定。
    • transform 1s ease-in-out で1秒間スムーズに移動。
  • slides.style.transform:
    • **translateX**を使って、スライド全体を左右に移動。
    • -index * 800 は現在のスライド位置を基に計算(1スライド = 800px幅)。

ループをスムーズにするためのリセット(resetPosition 関数)

const resetPosition = () => {
  slides.style.transition = 'none'; // トランジションを無効化
  if (currentIndex === totalSlides - 1) {
    currentIndex = 1; // ダミー最初から実スライド最初に移動
    slides.style.transform = `translateX(${-currentIndex * 800}px)`;
  } else if (currentIndex === 0) {
    currentIndex = totalSlides - 2; // ダミー最後から実スライド最後に移動
    slides.style.transform = `translateX(${-currentIndex * 800}px)`;
  }
};


  • slides.style.transition = 'none':
    • リセット時にトランジションを無効化。
    • トランジションが有効なままだと、リセット時にスライドがアニメーションし、ループが不自然に見えます。
  • 条件分岐 (if, else if):
    • currentIndex === totalSlides - 1:
      • 最後のスライド(ダミー最初)に到達した場合、最初の実スライドに移動。
      • currentIndex = 1 で位置を調整。
    • currentIndex === 0:
      • 最初のスライド(ダミー最後)に到達した場合、最後の実スライドに移動。
      • currentIndex = totalSlides - 2 で位置を調整。
  • slides.style.transform:
    • トランジションなしでスライド位置を瞬時に更新し、ループしているように見せます。

自動スライドとの連携

let autoSlide = setInterval(() => {
  currentIndex++;
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット
}, autoSlideInterval);


  • currentIndex++:
    • スライドを右に1つ進める。
  • moveSlide(currentIndex):
    • 現在のスライド位置に応じてアニメーションを実行。
  • setTimeout(resetPosition, 1000):
    • トランジションの終了(1秒後)にresetPositionを呼び出し、位置をリセット。

手動操作(ボタンやインジケータークリック)との連携

nextButton.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex++;
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット
  restartAutoSlide();
});

prevButton.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex--;
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット
  restartAutoSlide();
});



  • setTimeout(resetPosition, 1000):
    • トランジション終了後に位置をリセットし、不自然な動きを防ぎます。
  • restartAutoSlide():
    • ボタン操作後に自動スライドを再開します。

インジケータークリック時の調整

indicator.addEventListener('click', () => {
  clearInterval(autoSlide); // 自動スライドを一時停止
  currentIndex = i + 1; // ダミーを考慮してインデックスを調整
  moveSlide(currentIndex);
  setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット
  restartAutoSlide();
});



  • currentIndex = i + 1:
    • ダミースライドの影響を考慮して、インジケーターのインデックスを調整。
    • 実際のスライドに対応する正しい位置に移動。

レスポンシブに対応したスライドイン

今までのコードはピクセルを使った固定幅でしたので、CSSとJavaScriptを調整し、スライドショーをレスポンシブ対応に変えておきましょう。HTMLはスムーズに動くために1枚目と3枚目をダミー画像として設置したままです。

See the Pen レスポンシブ対応 by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.

CSSの調整

スライドショーの幅を固定値から割合(%)やビューポートの幅(vw)を使用した柔軟な指定に変更します。

.slideshow {
  width: 100%; /* スライドショー全体を画面幅に合わせる */
  max-width: 800px; /* 最大幅を指定 */
  height: auto;
  overflow: hidden;
  position: relative;
}

.slides {
  display: flex;
  transition: transform 1s ease-in-out;
}

.slideshow img {
  width: 100%; /* 画像の幅をスライドショーの幅に合わせる */
  height: auto; /* 縦横比を保持 */
  display:block;
}
.prev,
.next {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            background-color: rgba(0, 0, 0, 0.4);
            color: white;
            border: none;
            padding: 10px 20px;
            cursor: pointer;
            font-size: 16px;
            z-index: 10;
        }

        .prev {
            left: 10px;
        }

        .next {
            right: 10px;
        }

        /* インジケーター */
        .indicators {
            position: absolute;
            bottom: 15px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 10px;
        }

        .indicators .indicator {
            width: 15px;
            height: 15px;
            background-color: rgba(255, 255, 255, 0.5);
            border-radius: 50%;
            cursor: pointer;
        }

        .indicators .indicator.active {
            background-color: rgba(255, 255, 255, 1);
        }

JavaScriptの調整

スライドの幅を画面サイズに合わせて再計算するように変更します。スライド幅(slideWidth)を動的に取得し、ウィンドウリサイズ時にもスライド幅を再計算しておきましょう。

const slides = document.querySelector('.slides');
const slideImages = document.querySelectorAll('.slides img');
const prevBtn = document.querySelector('.prev');
const nextBtn = document.querySelector('.next');
const indicatorsContainer = document.querySelector('.indicators');

const totalSlides = slideImages.length; // 5枚(ダミー含む)
let currentIndex = 1; // 実際の最初のスライド位置(1が kv1)

// **スライドの幅を取得する関数(レスポンシブ対応)**
// `.slideshow` の幅を取得し、スライド幅として使用
const getSlideWidth = () => document.querySelector('.slideshow').clientWidth;

// **初期位置を設定する関数**
// スライドを最初の画像(currentIndex = 1)に瞬時に移動
const initPosition = () => {
    slides.style.transition = 'none'; // トランジションをOFF(瞬時に移動)
    slides.style.transform = `translateX(${-getSlideWidth() * currentIndex}px)`;
};

// **スライドを動かす関数**
// 指定した `index` へアニメーション付きで移動
const moveSlide = (index) => {
    slides.style.transition = 'transform 0.5s ease-in-out'; // スムーズにスライド
    currentIndex = index;
    slides.style.transform = `translateX(${-getSlideWidth() * currentIndex}px)`;
    updateIndicators(currentIndex); // インジケーターを更新
};

// **無限ループを実現する関数**
// `transitionend` イベント発火時に、ダミー画像の位置をリセット
const handleTransitionEnd = () => {
    if (currentIndex === 0) {
        // 先頭ダミー画像(index=0)なら、実際の最後の画像(index=3)に瞬時に戻す
        slides.style.transition = 'none';
        currentIndex = totalSlides - 2; // 5 - 2 = 3
        slides.style.transform = `translateX(${-getSlideWidth() * currentIndex}px)`;
    } else if (currentIndex === totalSlides - 1) {
        // 末尾ダミー画像(index=4)なら、実際の最初の画像(index=1)に瞬時に戻す
        slides.style.transition = 'none';
        currentIndex = 1;
        slides.style.transform = `translateX(${-getSlideWidth() * currentIndex}px)`;
    }
};

// **オートスライド(自動再生)を開始する関数**
let autoSlide;
const autoPlayInterval = 3000; // 3秒ごとにスライド

const startAutoPlay = () => {
    autoSlide = setInterval(() => {
        moveSlide(currentIndex + 1);
    }, autoPlayInterval);
};

// **オートスライド(自動再生)を停止する関数**
const stopAutoPlay = () => clearInterval(autoSlide);

// **オートスライドを再起動する関数**
// ユーザーが操作したときに、一時停止後に再スタート
const restartAutoPlay = () => {
    stopAutoPlay();
    startAutoPlay();
};

// **インジケーターを作成する関数**
// 画像の数(ダミーを除いた `totalSlides - 2`)分のインジケーターを作成
const createIndicators = () => {
    for (let i = 0; i < totalSlides - 2; i++) {
        const indicator = document.createElement('div');
        indicator.classList.add('indicator');
        if (i === 0) indicator.classList.add('active'); // 最初のスライドに `active` を追加

        // クリック時に該当スライドへ移動
        indicator.addEventListener('click', () => {
            moveSlide(i + 1); // ダミーを考慮して `+1`
            restartAutoPlay(); // 手動操作後に自動スライドを再開
        });

        indicatorsContainer.appendChild(indicator);
    }
};

// **インジケーターを更新する関数**
// 現在のスライド位置に応じて `active` クラスを付け替え
const updateIndicators = (index) => {
    const indicators = document.querySelectorAll('.indicator');
    indicators.forEach((indicator, i) => {
        indicator.classList.toggle('active', i === index - 1); // ダミーを考慮
    });
};

// **初期設定**
window.addEventListener('load', () => {
    initPosition(); // 初期位置を設定
    startAutoPlay(); // オートスライド開始
    createIndicators(); // インジケーターを作成
});

// **ウィンドウリサイズ時の処理**
// 画面サイズ変更時にスライド幅を再計算し、位置を修正
window.addEventListener('resize', () => {
    initPosition();
});

// **transition終了時のイベントリスナー**
slides.addEventListener('transitionend', handleTransitionEnd);

// **次へボタンの処理**
nextBtn.addEventListener('click', () => {
    if (currentIndex <= totalSlides - 1) {
        moveSlide(currentIndex + 1);
        restartAutoPlay(); // 手動操作後も自動スライドを再開
    }
});

// **前へボタンの処理**
prevBtn.addEventListener('click', () => {
    if (currentIndex >= 0) {
        moveSlide(currentIndex - 1);
        restartAutoPlay(); // 手動操作後も自動スライドを再開
    }
});
JavaScript スライドショー

この記事が気に入ったら
フォローしてね!

  • URLをコピーしました!
目次