Webページ内に画像を表示して、スクロールに合わせて徐々にその画像をぼかしていく効果を施すサンプルコードをご紹介します。
このサンプルではjQueryを使用して、1枚の画像だけでなく、ページ内にトリガーとなる画像が複数ある場合でもスクロールして可視エリアから外れるにつれて、それぞれの画像が徐々にぼやけていくようにしています。
INDEX
完成サンプル
See the Pen Blurring image as parallax scroll effect by digistate (@digistate) on CodePen.
HTMLの基本構成
画像エリアはfigure
タグで括り、img
タグと、そのキャプション要素となるfigcaption
で画像上に表示するキャプションテキストを構成しています。
<figure class="banner"> <div class="banner-wrapper" style="background-image:url(画像のURL);"> <img src="画像のURL" class="banner-img" /> </div> <figcaption> <h2>画像上に表示するタイトル</h2> <p>画像のキャプション</p> </figcaption> </figure>
CSSのfilter:blurで発生する画像の輪郭ぼけを補完
ぼかし方法は、figure
要素内のimg
要素をCSSのfilter:blur
で単純にブラーをかけているだけですが、これだとぼかし具合を大きくしたときに、どうしても画像の四隅の輪郭もぼやけて美しくありません。
そこで、今回のサンプルでは簡単にこの画像の縁のぼかしをごまかすため、img
要素の親要素であるdiv
にインラインでbackground-image
をセットし、同じ画像を背景画像として重ね、前面に表示される子要素のimgの画像をfilter:blur
でぼかしておきます。
背景画像は輪郭のぼやけを補完するために指定するため、後述するCSSで、背景画像とimg要素の画像の見た目は全く同じにする必要があります。
<div class="banner-wrapper" style="background-image:url(画像のURL)"> <!-- ↓フィルターでブラーを施す画像 --> <img src="画像のURL" id="one" class="banner-img" /> </div>
そうすると、フィルターでぼかしたimgの縁の部分は、divの背景画像で補完されたようになります。
背景(輪郭ぼけ補完)用のCSS
figure
のimg
要素の親要素であるdiv
(.banner-wrapper)のCSSは表示幅にフィットするようにします。
figure .banner-wrapper { background-position: center; background-size: 100% auto; /* 幅を100% */ }
前面(ぼかし用)の画像のCSS
最初に画像にはフィルターでぼかし(blur)を既にかけておき、 初期状態では不透明度を0にして見えなくしておきます。
スクロールでぼかしていくimg
要素のCSSも表示幅を100%で伸縮するスタイルで、さらにCSSでフィルターをかけていくときに発生するチラつきを防止するために-webkit-backface-visibility: hidden
を指定しておきます。
figure .banner-img { width: 100%; vertical-align: bottom; opacity: 0; /* Javascriptでスクロール時にブラーをかけるため、初期状態はみえなくする */ -webkit-backface-visibility: hidden; /* チラつき防止用 */ -webkit-filter: blur(8px); filter: blur(8px); }
これを、後述するjQueryのスクロールイベントでopacity
の値を変化させてフィルターを施した画像の不透明度を上げていくことで、ぼやけていく感じを演出します。
Javascriptの処理
まずは、共通で利用するグローバル変数で、ブラーをかける画像を持つfigure
要素と、ブラーをかける画像要素自体のセレクタ、そしてフィルターでぼやけていく速さをセットします。
var scrollBlur = { bannerSelector: '.banner', // figure要素 imgSelector: '.banner-img', // ぼかし用画像要素 degree: 2 // ぼかし速度の程度(1 = 等倍) }
DOMロード後に、ページ内のすべてのfigure
要素を一旦取得します。
$(function(){ banner = $(scrollBlur.bannerSelector); });
ここからは、jQueryでスクロールイベントを検知してフィルターでぼかした画像のopacity(不透明度)を上げていきます。
スクロールイベントの処理
jQueryでのスクロールイベントの処理は以下の書式で行えます。
$(window).on('scroll', function(){ // スクロール時に実行する処理 });
スクロールした分の垂直位置を取得するには以下で取得できます。
var scrollTop = $(window).scrollTop();
すべてのfigure要素について処理を行う
banner変数には、すべてのfigure
要素のjQueryオブジェクトが格納されているので、これをひとつずつ順番にループ処理します。
banner.each( function(){ // bannerオブジェクト内のfigure要素をひとつずつ処理 // 現在のfigure要素の垂直位置(px) var offsetTop = $(this).offset().top; : });
フィルターを施したimg要素を処理する
まずは、figure
のループ処理内に、さらに子要素であるimg
オブジェクトについて処理を行います。
// すべてのfigure要素のオブジェクトを順番に処理 banner.each( function(){ var offsetTop = $(this).offset().top; // figure要素内のぼかし画像の処理 $(this).find(scrollBlur.imgSelector).each(function(){ var current = $(this); // 現在の画像オブジェクト // 画像のopacityを変化させていく処理をここに }); });
実際にimg要素のopacityに変更を加える条件は、「スクロール位置(scrollTop)が対象のfigure要素の上部位置(offsetTop)を過ぎた位置」、かつ「スクロール位置が対象のfigure要素の最下部に到達するまでの位置」となります。
これを式にすると以下のようになります。
if ( scrollTop > offsetTop && scrollTop < offsetTop + current.height() ) { // opacityを変化 }
figure
要素内にあるimg
要素のopacityプロパティの値をスクロール値に応じて変化させることでフィルター済みの画像を徐々に表示させていきます。
opacityは 0.00〜1.00の間で変化をするため、figure
要素(img)の高さを1.00(100%)の変化量として、上部位置を0、下部位置が1となるような変化がつくように、opacityの値をスクロール位置に応じて 0〜1の範囲で換算します。
// (スクロールした位置 - figure要素の上部位置) / img要素の高さ var opacity = Math.floor( (scrollTop - offsetTop) / current.height() );
さらに、opacityの値として、これを小数第2位まで取得するようにします。
var opacity = Math.floor( (scrollTop - offsetTop) / current.height() * Math.pow( 10, 2 ) ) / Math.pow( 10, 2 );
これで、画像の上部が画面の上までスクロールするとopacityが0.00から正の方向に向かって増加し、画像の下部が画面の上までスクロールして見えなくなる位置まで来たときに、opacityは 1.00 となり、フィルターでぼかしたimg要素が完全に表示されます。
ただ、これだとスクロールして画像がどんどん見切れていく中でブラーがかかっていき、最後は完全に画像がスクロールされて見えなくなったときに初めてopacity:1
となるため、見た目的にはだんだん画像がぼやけていく様がわかりにくい感じがします。
そこで、上記のopacityの算出に、スクロールによるopacityの変化量を調整(増加)するための変数を設けてみます。
これが 変数degree
です。
// 2倍の速さでimgが完全に表示される(画像が半分見切れた時点でopacity:1となる)。 scrollBlur.degree = 2;
この調整用の変数degree
を組み込んで、最終的に変数opacity
の算出は以下の式となります。
var opacity = Math.floor( (scrollTop - offsetTop) * scrollBlur.degree / current.height() * Math.pow( 10, 2 ) ) / Math.pow( 10, 2 );
ここまではちょっと数学チックで若干ややこしかったですが、ここまで来ればあとはimg要素に得られたopacityの値をjQueryで書き換えてやるだけです!
// 変数opacityが 1 以下であればセット if ( opacity <= 1 ) { current.css('opacity', opacity); }
Javascriptのコード
ここまでのコードをまとめて、最終的に以下のJavascriptができあがります。
var scrollBlur = { bannerSelector: '.banner', imgSelector: '.banner-img', degree: 2 } var banner = null; $(function(){ banner = $(scrollBlur.bannerSelector); }); $(window).on('scroll', function(){ if (!banner[0]) return; var scrollTop = $(window).scrollTop(); banner.each( function(){ var offsetTop = $(this).offset().top; $(this).find(scrollBlur.imgSelector).each(function(){ var current = $(this); if ( scrollTop > offsetTop && scrollTop < offsetTop + current.height() ) { var opacity = Math.floor( (scrollTop - offsetTop) * scrollBlur.degree / current.height() * Math.pow( 10, 2 ) ) / Math.pow( 10, 2 ); if ( opacity <= 1 ) { current.css('opacity', opacity); } } }); }); });
今回のサンプルでは、スクロールによってopacity(不透明度)の値を変える、というシンプルなものでしたが、例えば変化する値を背景画像要素の background-position
に置き換えてやれば、簡単に自前でパララックススクロール効果を実現することもできますね!