スクロールと連動して背景画像が拡大するエフェクトを実装してみます。併せてbackground-size:cover; background-attachment:fixed;と同じエフェクトも実装してみます。jQueryで再現しますのでデスクトップとスマートフォンの両方で同じ動作になります。
2019年8月14日
スクロールして2枚目の背景画像がウインドウの上部まで来ると背景画像が固定され、その後のスクロール量に連動して背景画像が拡大されていきます。スクロール量がウインドウの高さ分に達するとまたスクロールが始まり3枚目の画像が見えてきますが、見え方としてはbackground-size:cover; background-attachment:fixed;と同じものになります。jQueryで再現していますのでデスクトップとスマートフォンの両方で同じ動作になります。
・DEMO画面はこちら
(注)スクロールイベントはFirefoxではコンソールに「このサイトはスクロールに対して配置を固定する効果が使用されています。これは非同期パンで正しく動作しない可能性があります。・・・」のメッセージが出ます。Firefoxではスクロールの途中でリロードなどをするとスクロール量が正しく認識できない場合があり、正しく表示できないことがありますのでご了承ください。
htmlとCSSについては以下のようになりますが、contents01〜contents03まで画像は全て背景画像として設定します。contents02(背景画像を拡大するブロック)についてはCSSではなくJavaScriptで背景画像を指定します。background-sizeもJavaScriptで指定しますのでCSSにその記述はありません。
また、contents02の高さがheight: 200vh;となっているのは100vh分のスクロールで背景画像を全画面に固定して拡大するときに下から次のブロック(contents03)が上がってこないようスペースを取る必要があるためです。
<div id="contents01">
<p>・・・</p>
</div>
<!-- 背景画像が拡大するブロック -->
<div id="contents02">
<div id="bg02">
<p>・・・</p>
</div>
</div>
<!-- background-attachment:fixed;をjQueryで再現するブロック -->
<div id="contents03">
<div id="mask">
<div id="bg03"></div>
</div>
<p>・・・</p>
</div>
#contents01 { position: fixed; top: 0; z-index: 1; height: 100vh; background-image:url('/images/bg01.jpg'); background-position: center center; background-size: cover; display: flex; align-items: center;}
#contents01 p { padding: 20px; color: #fff; line-height: 2.0;}
#contents02 { position: relative; top: 100%; z-index: 2; height: 200vh; }
#bg02 { width: 100%; height: 100vh; background-position: center center; background-repeat: no-repeat; display: flex; align-items: center;}
#contents02 p { padding: 20px; color: #fff; line-height: 2.0;}
#contents03 { position: relative; top: 100%; z-index: 3; height: 150vh;}
#mask { position: relative; width: 100%; height: 50vh; overflow: hidden; }
#bg03 { position: absolute; left: 0; width: 100%; height: 100vh; background-image:url('/images/bg03.jpg'); background-position: center center; background-size: cover;}
#contents03 p { width: 100%; height: 100vh; padding: 0 20px; color: #fff; line-height: 2.0; background: #64c11c; display: flex; align-items: center;}
背景画像の拡大はCSSのbackground-sizeの値を制御していくことで今回は実現します。全画面表示の場合はbackground-size: cover;が便利ですがこれでは数値をいじれないので、background-size: 100% auto;またはbackground-size: auto 100%;の数値の部分をいじっていくことにします。どちらのケースになるかは画面の縦横比と背景画像の縦横比を比べて決めることになります。
// 画面の方が横長の場合
background-size: 100% auto;を制御
// 背景画像の方が横長の場合
background-size: auto 100%;を制御
それではこの部分のjQueryを書いてみたいと思います。背景画像はJavaScriptで読み込んで処理したいと思いますのでイメージオブジェクトを生成した後、img.onloadで画像の読み込みが完了した後にサイズを取得して縦横比を算出しています。
const $window = $(window);
const windowWidth = $window.width();
const windowHeight = $window.height();
// 画面の縦横比
const ra02 = windowWidth / windowHeight;
const img = new Image();
img.onload = function() {
const imgWidth = img.width;
const imgHeight = img.height;
// 画像の縦横比
const ra01 = imgWidth / imgHeight;
}
img.src = "/images/bg02.jpg";
前述したように画面と画像の縦横比を比べて、画面の方が横長の場合はbackground-size: 100% auto;を、画像の方が横長の場合はbackground-size: auto 100%;を制御しています。今回はスクロール量を10で割った値を拡大分の%としていますので、例えば500pxスクロールしたら150%に背景画像が拡大されるようになっています。
const $bg02 = $('#bg02');
const bg02OffsetTop = $bg02.offset().top;
const bg02Height = $bg02.height();
const $bg03 = $('#bg03');
const bg03Height = $bg03.height();
const img = new Image();
img.onload = function() {
const imgWidth = img.width;
const imgHeight = img.height;
// 画像の縦横比
const ra01 = imgWidth / imgHeight;
// 背景画像の設定
$bg02.css({
backgroundImage: `url(${img.src})`
});
$window.scroll(function() {
const dy = $(this).scrollTop();
// 背景画像が拡大される領域
if ((dy >= bg02OffsetTop) && (dy < bg02OffsetTop + bg02Height)) {
if (ra01 >= ra02) {
$bg02.css({
position: 'fixed',
top: 0,
backgroundSize: `auto ${100 + (dy - bg02OffsetTop) / 10}%`
});
} else {
$bg02.css({
position: 'fixed',
top: 0,
backgroundSize: `${100 + (dy - bg02OffsetTop) / 10}% auto`
});
}
}
});
}
img.src = "/images/bg02.jpg";
この部分の効果は全画面に背景画像を表示した上でスクロールにしたがってマスクが移動することで実現しています。下図の左のような位置にマスク(黒い部分)があるときには右のように見えているということです。
背景画像($bg03)はマスクに対して絶対位置指定となりますので、マスクがスクロールにともなって移動した分と同じ量を逆方向に移動してあげることで、背景画像が画面に対して固定されているように見えます。マスクの使い方については『jQueryでスクロールと連動した画像の切り替え(斜めトリミング)を実装』もご参考ください。
$window.scroll(function() {
const dy = $(this).scrollTop();
// 背景画像が拡大される領域
if ((dy >= bg02OffsetTop) && (dy < bg02OffsetTop + bg02Height)) {
// 背景画像を拡大する処理(前述)
// background-attachment:fixed;をjQueryで再現する領域
} else if (dy >= bg02OffsetTop + bg02Height) {
$bg02.css({
position: 'fixed',
top: 0
});
// 背景画像の位置を画面に対して固定
$bg03.css({
top: `${-bg03Height + (dy - (bg02OffsetTop + bg02Height))}px`
});
} else {
// 上記以外の領域での処理
}
});
$window.trigger('scroll');
最後に通しでjQueryの記述を掲載しておきます。
※DEMO画面では以下のコードをbabelでコンパイルしたものを使用しています。
$(function() {
'use strict';
const $window = $(window);
const windowWidth = $window.width();
const windowHeight = $window.height();
const ra02 = windowWidth / windowHeight;
const $bg02 = $('#bg02');
const bg02OffsetTop = $bg02.offset().top;
const bg02Height = $bg02.height();
const $bg03 = $('#bg03');
const bg03Height = $bg03.height();
const img = new Image();
img.onload = function() {
const imgWidth = img.width;
const imgHeight = img.height;
const ra01 = imgWidth / imgHeight;
$bg02.css({
backgroundImage: `url(${img.src})`,
});
$window.scroll(function() {
const dy = $(this).scrollTop();
if ((dy >= bg02OffsetTop) && (dy < bg02OffsetTop + bg02Height)) {
if (ra01 >= ra02) {
$bg02.css({
position: 'fixed',
top: 0,
backgroundSize: `auto ${100 + (dy - bg02OffsetTop) / 10}%`
});
} else {
$bg02.css({
position: 'fixed',
top: 0,
backgroundSize: `${100 + (dy - bg02OffsetTop) / 10}% auto`
});
}
} else if (dy >= bg02OffsetTop + bg02Height) {
$bg02.css({
position: 'fixed',
top: 0
});
$bg03.css({
top: `${-bg03Height + (dy - (bg02OffsetTop + bg02Height))}px`
});
} else {
$bg02.css({
position: 'relative',
top: 0
});
}
});
$window.trigger('scroll');
};
img.src = "/images/bg02.jpg";
});
以上で「スクロールと連動した背景画像の拡大と固定(レスポンシブ)」の解説を終わります。