【CSS/JavaScript】スクロールに応じて画像切り替え

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

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

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

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

ウェブデザインをしたことがある人であれば、このようなデザインを作りたいと考える人もいるかと思います。

しかし、いざコードを書いてもなかなか作成することができず、挫折したも多いことでしょう。

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

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

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

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

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

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

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

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

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%;
    }

Webデザインを学びたい

HTMLやCSSなどはマークアップ言語と呼ばれてプログラミングと異なります。
プログラミングは指示通りに処理がされると完成ですが、
フロントエンドは、Webデザインも重要になります。

デザインの幅を広げたいけれど、コーディングに悩んでいませんか?
美しいデザインを実現するためには、常に新しいスキルを身につけることが重要です。

Udemyは動画でHTMLやCSSの基礎から最新のデザインテクニックを実践形式で学べます。
購入した講座は再生・停止・スキップなどが可能なオンデマンド形式なので、
講座中に独自でアレンジして試行錯誤ができます。

Udemyの特徴
  • プロのエンジニアによる講習が受けられる
  • 自分のペースで学習を進められる
  • オンデマンド形式だから何度でも視聴可能
  • 不満足なコースは視聴していても返金可能返金ポリシー

HTML/CSS/JavaScriptに興味がある人はUdemy学習を取り入れましょう。
数多くある講座の中から特におすすめな講座を3つ紹介します。

HTML、CSS、JavaScriptの学習ができて、フレームワークの扱い方、セキュリティー、その他プログラミング言語を学ぶために必要な考え方など幅広く情報を網羅している。

WEBシステムの仕組みと挙動、システム構築などWEBシステムの基礎の学習が可能。

JSとCSSに焦点を当てた講座。基礎レベルから実務レベルまで網羅していて、最終的にアニメーションやコードの最適化、安定化について学ぶ。

解説

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をコピーしました