interserctionObserverでレスポンシブ対応|スクロールアニメーション

intersectionObserverでのブラウザ幅が変更された時の実装方法で、PC表示のアニメーションのタイミングと、スマホ表示の時にアニメーションのタイミングを変えたい時の設定方法でJavascriptでのresizeイベントを使っての方法を紹介したいと思います。

実装の仕様

・intersectionObserverを使い、アニメーションを実装(intersectionObserverなのでライブラリ不要)

・resizeイベントを使い、ブラウザ幅がリアルタイムで変更された時にアニメーションのタイミングを変更する

intersectionObserverを使った完成コード

See the Pen blog_responsive_observer by 殿村 真史 (@ryngytsw-the-encoder) on CodePen.

codepenを開いて確認するとアニメーションのタイミングの変更がわかりやすいです。

intersectionObserverのHTMLを解説します

<section class="section">
    <div class="section__imgBox action">
      <img src="https://dl.dropbox.com/scl/fi/eab2fb8yryqkuymfu3lcl/dummy_img.png?rlkey=h7xejmw9hg2gv1t1hmyd3n6g1&st=7yyd5dj5&dl=0" alt="" class="section__img" width="400" height="302">
    </div>
  </section>
  <section class="section">
    <div class="section__imgBox action">
      <img src="https://dl.dropbox.com/scl/fi/eab2fb8yryqkuymfu3lcl/dummy_img.png?rlkey=h7xejmw9hg2gv1t1hmyd3n6g1&st=7yyd5dj5&dl=0" alt="" class="section__img" width="400" height="302">
    </div>
  </section>
  <section class="section">
    <div class="section__imgBox action">
      <img src="https://dl.dropbox.com/scl/fi/eab2fb8yryqkuymfu3lcl/dummy_img.png?rlkey=h7xejmw9hg2gv1t1hmyd3n6g1&st=7yyd5dj5&dl=0" alt="" class="section__img" width="400" height="302">
    </div>
  </section>
  <section class="section"></section>

HTMLはシンプルですが、intersectionObserverの発火用クラスにimgタグを包括しているdivタグにactionクラスを設置しています。発火後はactionクラスが付いているdivタグにis-fadeInクラスを付加してアニメーションが発動する仕様にしています。

intersectionObserverのCSSを解説します

.section {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  width: 100%;
  height: 100vh;
}

.section__imgBox {
  max-width: 30%;
  width: 100%;
}
@media (max-width: 768px) {
  .section__imgBox {
    max-width: 50%;
  }
}

.section__imgBox.action {
  opacity: 0;
  visibility: hidden;
  transform: translateY(50px);
  transition: opacity 1s, visibility 1s, transform 1s ease-out;
}

.section__imgBox.is-fadeIn {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.section__img {
  width: 100%;
  height: auto;
}

19行目で、CSSはimgタグを囲っているdivタグの.section__imgBoxに.actionクラスを付けて、.actionクラスをアニメーションのトリガーにしております。

26行目の設定でintersectionObserverで.actionクラスが付いた要素が設定した領域に入ると、.actionクラスが付いたdivタグに.is-fadeInクラスが付加されるのでアニメーションがCSS開始されるように設定しました。

intersectionObserverでレスポンシブな要素の監視を設定する

'use strict';

let observer;

// IntersectionObserverを初期化する関数
function initObserver(num) {
  const targets = document.querySelectorAll('.action');

  const options = {
    root: null,
    rootMargin: `0px 0px ${num} 0px`,
  };

  function callback(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-fadeIn');
      } else {
        entry.target.classList.remove('is-fadeIn');
      }
    });
  }

  observer = new IntersectionObserver(callback, options);

  targets.forEach(target => {
    observer.observe(target);
  });
}

// IntersectionObserverを解除する関数
function removeObserver() {
  if (observer) {
    observer.disconnect();
    observer = null;
  }
}

// IntersectionObserverを再初期化する関数
function resetObserver(num) {
  removeObserver();
  initObserver(num);
}

//ロード時にIntersectionObserverを初期化する処理
window.addEventListener('DOMContentLoaded', function() {
  if (window.innerWidth > 768) {
    initObserver('-20%');
  } else {
    initObserver('-50%');
  }
});

//リサイズ時にIntersectionObserverを再初期化する処理
window.addEventListener('resize', function() {
  if (window.innerWidth > 768) {
    resetObserver('-20%');
  } else {
    resetObserver('-50%');
  }
});

3行目では、グローバルで使える変数observerを値を入れないで宣言しておきます。こうすることで、どの関数内でもobserver変数を使えるようにしておきます。

6行目ではintersectionObserverを初期化する関数を作っておき、observer変数に格納しておきます。intersectionObserverを起動する場合はこの関数を使用します。このinitObserver関数には引数numとして、値を受け取れるようにしておき、発火タイミングを変えれるようにしておきます。

11行目のrootMarginでその引数numを受け取るようにしているのですが、rootMarginは監視領域にmarginを指定できます。10行目でroot: nullとすることで監視領域がブラウザ領域となるので、そこにネガティブマージン(マイナスのmargin)を設置することで、監視対象が監視領域にどれくらい入ったらアニメーション実行するかを設定できます。

rootMarginは下記の順番で指定して、単位を入れておかないとエラーになるのでご注意を。
rootMargin: '上方向のmargin 右方向のmargin 下方向のmargin 左方向のmargin'

32行目では、intersectionObserverを解除する関数を定義しています。33行目のif文でobserverがあれば(初期化されていれば)、34行目でobserver.disconnect()というメソッドを使いobserverを解除します。一応、後のif文で判定をちゃんとするために、35行目でobserver = null;として変数の中身をちゃんと空にしておきます。

40行目では、レスポンシブ対応でブラウザ幅が変わった時のためにintersectionObserverをリセットする関数を定義しておきます。resetObserver関数ではnumという引数を受け取れるようにしておき、リセット時に監視タイミングの値を受け取れるようにしておき、resetObserver関数内では先ほど作った、解除する関数removeObserverを実行してから、初期化する関数initObserverを実行します。その際はinitObserverで監視タイミングの値のnum引数を受け取れるようにしておきました。

46行目ではページ表示時のintersectionObserverの起動の処理を書いていて、47行目のif文でwindow.innerWidthでブラウザ領域の幅を取得できるので、ブラウザ表示幅が768以上なら、初期化する関数initObserverで-20%の値を受け取り(PCサイズはアニメーションタイミングを速くしてます)、768以下なら-50%の値を受け取ります。

55行目ではブラウザ幅の領域が動的に切り替わった時、例えばスマホを横向きにした場合に横幅が広がってPCレイアウトになった時に動作する処理を書いています。(最近のスマホは縦に長いので横向きにするとPCレイアウトになる)

イベント部分にresizeとすることで画面の横幅が動的に変わると実行するイベントなので、先ほどの47行目と同じ条件のif文を設置して、ブラウザ横幅が768以上ならintersectionObserverをリセットする関数resetObserverを実行して、引数に-20%を指定して、768以下なら引数に-50%を入れます。このようにすることで動的にブラウザ幅が変更になった場合も監視タイミングの変更が可能になりました。

今回は、Swiper.jsライブラリを使いスマホのみでスライダーを実装の解説をさせていただきました。
また疑問点やおかしな箇所などありましたら、記事のコメント欄やフォームなどからメッセージをいただけますとありがたいです。

この記事を書いた人

アバター

トノムラマサシ

masashi
Webサイト屋兼ブロガー|過去に企業常駐などを経て現在はフリーランスでディレクションとコーダーとして活動しております。JS大好きなのでJSの仕事依頼お待ちしております。京都在中で5人家族。