【更新日 :

【JavaScript】Intersection Observer + scrollイベントでパララックスを実装する

Category: JavaScript

Intersection Observer + scrollイベントでパララックスを実装したサンプルです。
画面に入った要素のみscrollイベントを発火させるので、scrollイベントのみの実装よりは低負荷になっていると思います。


※以前実装した以下のコードをベースにしています。

動作サンプル

表示エリアからはみ出している量だけ画像が動きます。
動く量はウィンドウの高さに応じて算出しているので、スクロール中に要素がエリアから飛び出したりもしません。


See the Pen
Intersection Observer + Scroll Event Parallax Vanilla JS
by Kazuma Sakata (@sakata-kazuma)
on CodePen.


HTML

<div class="js-parallax-elm-box">
  <div class="js-parallax-elm">
    <img src="画像パス" alt="">
  </div>
</div>

表示エリア(.js-parallax-elm-box)の中に、動かす要素(.js-parallax-elm)を内包してください。
上記は画像を格納していますが、何が入っていても動きます。

CSS

.js-parallax-elm-box {
  overflow: hidden;
}
.js-parallax-elm-box img {
  display: block;
}

表示エリアからはみ出している要素を隠すため、最低限overflow: hidden;を設定してください。
※表示エリアのheightは実装するデザインに応じて適宜設定してください。

パララックス関数

//Intersection Observer + Scroll Event Parallax Vanilla JS
function parallax() {
  'use strict';

  //class設定
  //表示エリア class
  const targetClass = '.js-parallax-elm-box';
  //動かす要素 class
  const childClass = '.js-parallax-elm';

  //表示エリア取得
  const targets = Array.prototype.slice.call(document.querySelectorAll(targetClass),0);
  //表示エリアが存在するかチェック
  if(targets.length === 0) {
    return;
  }

  //observer設定
  let observer = new IntersectionObserver(observerFunc, {
    root: null,
    rootMargin: '0px',
    threshold: 0
  });

  //ウィンドウの高さ取得
  let winH = window.innerHeight;
  //スクロール量(ウィンドウの上端)取得
  let scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  //スクロール量(ウィンドウの下端)取得
  let scrollBottom = scrollTop + winH;

  //初期設定
  let setListener =  [];
  let settings =  [];
  targets.forEach((target,index) => {
    //Listener設定取得用の判別番号をセット
    target.setAttribute('data-index',index);
    //動かす要素を取得
    const child = target.querySelector(childClass);
    //動かす要素が存在するかチェック
    if(!child) {
      return;
    }
    //処理用の設定を追加
    settings.push({
      child: child,
      //比率取得:スクロールできる最大量 / (ウィンドウの高さ + 表示エリアの高さ)
      scrollRatio: (child.clientHeight - target.clientHeight) / (winH + target.clientHeight)
    });
    //scrollイベントへ渡すlistener設定
    setListener.push(
      {
        target: target,
        handleEvent: function handleEvent () {
          requestAnimationFrame(parallaxFunk.bind(target));
        }
      }
    );
    //初期表示の位置調整
    requestAnimationFrame(parallaxFunk.bind(target));
    //observer監視開始
    observer.observe(target);
  });

  //高さ更新
  let resizeID;
  window.addEventListener('resize', function() {
    clearTimeout(resizeID);
    resizeID = setTimeout(() => {
      //ウィンドウの高さ取得
      winH = window.innerHeight;
      //各要素の高さ更新
      targets.forEach((target,index) => {
        //動かす要素が存在するかチェック
        if(!settings[index].child) {
          return;
        }
        //比率取得:スクロールできる最大量 / (ウィンドウの高さ + 表示エリアの高さ)
        settings[index].scrollRatio = (settings[index].child.clientHeight - target.clientHeight) / (winH + target.clientHeight);
      });
    }, 200);
  });

  //スクロール量更新
  window.addEventListener('scroll', function() {
    //スクロール量(ウィンドウの上端)取得
    scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    //スクロール量(ウィンドウの下端)取得
    scrollBottom = scrollTop + winH;
  }, {passive: true});

  //observer処理
  function observerFunc(entries) {
    entries.forEach(entry => {
      //listener設定取得
      const listener =  setListener[entry.target.getAttribute('data-index')];
      if(entry.isIntersecting) {
        //画面に表示されている要素のみスクロールイベントへ追加
        window.addEventListener('scroll', listener, {passive: true});
      } else {
        //画面外のときはスクロールイベント削除
        window.removeEventListener('scroll', listener, {passive: true});
      }
    });
  }

  //パララックス位置調整関数
  function parallaxFunk() {
    //番号取得
    const index =  Number(this.getAttribute('data-index'));

    //表示エリアの位置取得
    const targetPosi =  this.getBoundingClientRect().top + scrollTop;

    //ウィンドウの高さに対するスクロール量を取得(小数点第2以下は四捨五入)
    const setVal = ((scrollBottom - targetPosi) * settings[index].scrollRatio).toFixed(1);

    //スクロール値を設定
    settings[index].child.style.transform = 'translate3d(0,'+ (setVal * -1) +'px,0)';
  }
}

対象class名を変更したい場合は、targetClass「表示エリア class」と、childClass「動かす要素 class」を編集してください。

関数の呼び出し

任意の場所で以下を実行し関数を呼び出してください。

//パララックス呼び出し
parallax();

関連リンク

参考リンク

Category : JavaScript