スクロールすると画像が動く。
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%;
}
UdemyでWebデザインを学習
Udemyは、オンデマンド式の学習講座です。
趣味から実務まで使えるおすすめの講座を紹介します。
- ちゃんと学ぶ、WordPress テーマ開発講座
WordPressの自作テーマを作成してWordPressの仕組み理解。
WordPressで高度なカスタマイズをしたい人におすすめ。
- 【世界で70万人が受講】Web Developer Bootcamp 2023(日本語版)
フロントエンド言語でフレームワークの扱い方、セキュリティーを学習。
Web開発を学びたい初心者から経験者までにおすすめ。
- 【PHP, MYSQL, Apache】ガチで学びたい人のためのWEB開発の基礎(バックエンド編)
Webシステムの仕組みと挙動、システム構築などを学習。
サーバーに関わる知識を付けたい人におすすめ。
- 【2023年最新】【JavaScript&CSS】ガチで学びたい人のためのWEB開発実践入門(フロントエンド編)
基礎レベルから実務レベルまで網羅していて、JSとCSSに焦点を当てた講座。
Webデザインをしたい人におすすめ。
解説
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とは、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>