【CSS/JavaScript】スクロールに合わせて画像切り替え

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと以下の実行ができます

スクロールすると画像が動く。

スクロールすると背景が動く

Appleの「Airpots pro」やUnityのホームページなどで、
スクロールに応じて背景が動くパララックスがあります。

ウェブデザインはユーザーの目を引くので、
個性的にすることがおすすめです。

このデザインに惚れて何度も挑戦しましたが挫折を繰り返し、
2年の時を経てようやく完成させることができました。

本記事では、スクロールに応じて、背景が変化するウェブデザインを作成します。

本記事は次の人におすすめ
  • 人目を引くwebデザインを作成したい。
  • スクロールすると、背景が動く(画像が切り替わる)パララックスを作成したい。
スポンサーリンク

スクロールで背景を動かす

本パララックス作成の着想は、パラパラ漫画からきています。

ご存じの通りパラパラ漫画は、1枚1枚の絵や画像をつなげて映像のように見えるものです。
アニメで絵が動いていてみえるのは、1枚1枚アニメーターの方が手掛けた絵をつなげているためです。

パラパラ漫画

本製作でもパラパラ漫画の画像を複数用意して、
スクロールするごとに画像を変化させて背景を動かします。

つなげる画像は「AvUtil」の「連番BMP」を使用すれば、だれでも簡単に作成することができます。

aviUtlで画像出力を紹介しています。

AvUtilで画像を出力

初めにAvUtilで画像を出力します。

AvUtilでアニメーションづくり

本記事では、以下のような動きをする画像を作成します。

動作例

「ファイル」から「プラグイン出力」の「連番bmp」にて画像を出力します。

出力した画像は、管理のためにデスクトップ直下ではなく、フォルダー内に保管してください。

出力した画像

ここまでで、画像を用意してきたので、ここからはコードを書いていきます。
HTML,CSS,JavaScriptはいずれも子のフォルダー内に書いてください。

画像とコードの保存場所が別々では、パスが長くなり挙動が想定通りにならない可能性があります。

また、AvUtilでは出力する画像形式は文字通りbmpです。

bmpは非圧縮形式であるため高品質な画像表現ができますが、
サイズは他の形式に比べて大きくなりやすいです。

したがって、画像形式を変換することをおすすめします。

画像形式はPythonのpillowとosライブラリを使用することで、変換することができます。

画像の変換について紹介しています。

HTML

  <div class="container">
    <img src="image_0000.jpg" alt="Image" class="main-image">
    <img src="" alt="Alternate Image" class="alternate-image">
  </div>

解説

class「container」で画像を含むコンテナを表します。
<img>要素で、image_0000.jpgファイルを指定します。

また、class属性を「main-image」で指定します。
もう一方の<img>要素は、src属性を空の値が指定しているため画像が表示されません。
class属性はalternate-imageというクラスが指定します。

CSS

 .container {
      width: 50%;
      height: 50%;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      overflow: hidden;
    }

    .main-image {
      position: relative;
      display: block;
      max-width: 100%;
    }

    .alternate-image {
      display: none; /* 最初は非表示にする */
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
    }

UdemyでWebデザインを学習

Udemyは、オンデマンド式の学習講座です。
趣味から実務まで使えるおすすめの講座を紹介します。

ビックセール開催中(10月25日まで)
対象のコースが1800円から(最大95%OFF)

多彩な講座から自分に合った講座を探そう!

最大94%OFF

終了まで

時間

解説

Class「body」はHTML文書の要素に対するスタイルを指定しています。
「height」を 20000pxにします。
これにより、適当な高さが設定され、スクロールが有効になります。

Class「.container」はbodyに対するスタイルを指定しています。
「width」・「height」を50%にします。幅と高さを親要素の50%に設定しています。(実際ウェブサイトに導入するときは100%にしてください。)
「position」をfixedにします。要素の位置を固定位置に設定しています。
「top」を50%にします。上端をビューポートの垂直中央に配置します。
「left」を 50%にします。左端をビューポートの水平中央に配置します。
「transform」をtranslate(-50%, -50%)にします。
水平方向に50%、垂直方向に50%移動させます。
これにより、要素の中心がビューポートの中央に配置されます。
「overflow」をhiddenにします。
.container要素の内容が親要素の範囲を超えた場合、表示範囲内に収めるために内容を非表示にします。

Class「.main-image」はimage_0000.jpg(初めの画像)に対するスタイルを指定しています。「position」は relativeにします。親要素(.container)に対して相対的な位置に配置します。
「display」はblockにします。 .main-image要素をブロックレベル要素として表示します。
「max-width」は 100%にします。最大幅を親要素の幅に合わせます
これにより、画像が親要素内に収まるようになります。

Class「.alternate-image」は“Alternate Image”対するスタイルを指定しています。
「display」は noneにします。.alternate-image要素を非表示にします。
初期状態ではこの要素は表示されません。
「position」はabsoluteにします。親要素に対して絶対的な位置に配置します。
「top」は 0にします。上端を親要素の上端に配置します。
「left」は 0にします。左端を親要素の左端に配置します。
「width」は100%にします。.alternate-image要素の幅を親要素の幅に合わせます
これにより、要素が親要素全体を占めるようになります。

JavaScript

window.addEventListener('DOMContentLoaded', function() {
  // DOMのロードが完了した時点で実行される処理

  var container = document.querySelector('.container');
  var mainImage = document.querySelector('.main-image');
  var alternateImage = document.querySelector('.alternate-image');
  var currentImageIndex = 0; // 現在の画像インデックスの初期値を0に設定
  var scrollOffset = 0;

  function loadNextImage() {
    // 次の画像をロードする関数

    var paddedNumber = currentImageIndex.toString().padStart(4, '0');
    alternateImage.src = 'image_' + paddedNumber + '.jpg';
    alternateImage.alt = 'Alternate Image ' + paddedNumber;
  }

  function scrollHandler() {
    // スクロールイベントを処理する関数

    var containerRect = container.getBoundingClientRect();
    var scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
    var scrollDiff = scrollPosition - scrollOffset;

    console.log('Scroll Position:', scrollPosition);

    if (scrollDiff >= 45) {
      // スクロール量が45px以上の場合(正の方向にスクロール)

      scrollOffset = scrollPosition;
      if (currentImageIndex <= 18) { // 画像の枚数(最後の画像の数字-1)
        currentImageIndex++;
        loadNextImage();
      }
    } else if (scrollDiff <= -45) {
      // スクロール量が-45px以下の場合(負の方向にスクロール)

      currentImageIndex = Math.max(currentImageIndex - 1, 0);
      var paddedNumber = currentImageIndex.toString().padStart(4, '0');
      alternateImage.src = 'image_' + paddedNumber + '.jpg';
      alternateImage.alt = 'Alternate Image ' + paddedNumber;
      scrollOffset = scrollPosition;
    }

    if (scrollPosition > containerRect.top) {
      // スクロール位置がコンテナの上端を超えた場合

      mainImage.style.display = 'none';
      alternateImage.style.display = 'block';
    } else {
      mainImage.style.display = 'block';
      alternateImage.style.display = 'none';
    }
  }

  window.addEventListener('scroll', scrollHandler);
  loadNextImage();
});

解説

初めに、DOMContentLoadedイベントをHTMLドキュメントの解析が完了し、
DOMの構築が完了した時点で発生するイベントを作成します。

このイベントに対してコールバック関数が登録されています。

DOMとは?

DOMとは、Webドキュメントのプログラミングインターフェイスです。

プログラムがドキュメントの構造、スタイル、コンテンツを変更できるように
ページを表すときにDOMを使用します。

ドキュメントオブジェクトモデル (DOM) はウェブ文書のためのプログラミングインターフェイスです。ページを表現するため、プログラムが文書構造、スタイル、内容を変更することができます。 DOM は文書をノードとオブジェクトで表現します。そうやって、プログラミング言語をページに接続することができます。

ウェブページは、ブラウザーのウィンドウに表示したり、 HTML ソースとして表示したりすることが可能な文書です。どちらの場合も同じ文書であり、ドキュメントオブジェクトモデル (DOM) で操作することができます。 DOM はウェブページの完全なオブジェクト指向の表現で、 JavaScript のようなスクリプト言語から変更することができます。

DOM の紹介 – Web API – MDN Web Docs – Mozilla

変数「container」、「mainImage 」、「alternateImage」にそれぞれのクラス要素を取得します。
取得には、「document.querySelector(‘要素’)」を使います。

変数「currentImageIndex」を0にします。
現在表示されている画像のインデックスを保持する変数です。

変数「scrollOffset」を 0にします。
前回のスクロール位置を保持する変数です。

function loadNextImage() {…}は、次の画像をロードするための関数です。
現在の画像インデックスを元に、alternateImage要素のsrc属性とalt属性を設定します。

function scrollHandler() {…}は、スクロールイベントを処理するための関数です。
スクロール位置やスクロール量を計算し、必要な処理を実行します。

scrollイベント「window.addEventListener(‘scroll’, scrollHandler)」で、scrollHandler関数をイベントリスナーとして登録します。
つまり、スクロールイベントが発生した時にscrollHandler関数が実行されます。

loadNextImage()は、初期画像をロードするために、loadNextImage関数を実行しています。

実演

それでは、実際に動作を確認します。

マウスカーソルを上にしてスクロールしても動きますが動作がカクカクです。
スクロールバーで操作するとスムーズに動きます。

ソースコード(一括)

<!-- スクロールyに応じて画像切り替え -->
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      height: 2000px; /* 適当な高さを設定し、スクロールを有効にする */
    }

    .container {
      width: 50%;
      height: 50%;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      overflow: hidden;
    }

    .main-image {
      position: relative;
      display: block;
      max-width: 100%;
    }

    .alternate-image {
      display: none; /* 最初は非表示にする */
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
    }
  </style>
  <title>Document</title>
  <script>
window.addEventListener('DOMContentLoaded', function() {
  // DOMのロードが完了した時点で実行される処理

  var container = document.querySelector('.container');
  var mainImage = document.querySelector('.main-image');
  var alternateImage = document.querySelector('.alternate-image');
  var currentImageIndex = 0; // 現在の画像インデックスの初期値を0に設定
  var scrollOffset = 0;

  function loadNextImage() {
    // 次の画像をロードする関数

    var paddedNumber = currentImageIndex.toString().padStart(4, '0');
    alternateImage.src = 'image_' + paddedNumber + '.jpg';
    alternateImage.alt = 'Alternate Image ' + paddedNumber;
  }

  function scrollHandler() {
    // スクロールイベントを処理する関数

    var containerRect = container.getBoundingClientRect();
    var scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
    var scrollDiff = scrollPosition - scrollOffset;

    console.log('Scroll Position:', scrollPosition);

    if (scrollDiff >= 45) {
      // スクロール量が45px以上の場合(正の方向にスクロール)

      scrollOffset = scrollPosition;
      if (currentImageIndex <= 18) { // 画像の枚数(最後の画像の数字-1)
        currentImageIndex++;
        loadNextImage();
      }
    } else if (scrollDiff <= -45) {
      // スクロール量が-45px以下の場合(負の方向にスクロール)

      currentImageIndex = Math.max(currentImageIndex - 1, 0);
      var paddedNumber = currentImageIndex.toString().padStart(4, '0');
      alternateImage.src = 'image_' + paddedNumber + '.jpg';
      alternateImage.alt = 'Alternate Image ' + paddedNumber;
      scrollOffset = scrollPosition;
    }

    if (scrollPosition > containerRect.top) {
      // スクロール位置がコンテナの上端を超えた場合

      mainImage.style.display = 'none';
      alternateImage.style.display = 'block';
    } else {
      mainImage.style.display = 'block';
      alternateImage.style.display = 'none';
    }
  }

  window.addEventListener('scroll', scrollHandler);
  loadNextImage();
});
  </script>
</head>
<body>
  <div class="container">
    <img src="image_0000.jpg" alt="Image" class="main-image">
    <img src="" alt="Alternate Image" class="alternate-image">
  </div>
</body>
</html>

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