Intersection Observerを使って
画面内に要素が入ったら
アニメーションさせる

はじめに

画面スクロール時に特定の要素が画面内に入ったら読み込んだり、アニメーションを適用して表示させたりしたい時に使えるAPIのメモ。
こういった実装はこれまでscrollイベントで実装されてきましたが、スクロールするたびにイベントが発生するので負荷がかなり大きく、画面が重くなる原因になりがちでした。そのためrequestAnimationFrameを使ってパフォーマンスを向上させる方法などがググるとたくさん紹介されています。それでもまだ重く感じたり負荷が気になっていたのですが、それを解決する「Intersection Observer API」というものがあることを知ってからはこちらで実装するようになりました。

Intersection Observer APIとは?

直訳すると「交差点監視」という意味ですが、その名の通り「要素同士の交差を判定できる」APIです。指定した要素と要素が交差した時だけイベントが発生するので、これまでのようにスクロールするたびに画面のスクロール位置を取得して目標の要素の位置や高さを取得して条件分岐して・・・というスクロールイベントの闇から解放されます。笑

対応ブラウザ

IEなど一部のブラウザで使えませんが、Edge、Chrome、Firefox、Safari、iOS Safariなどのモダンブラウザの最新バージョンで使えます。もちろんpolyfillがありますので、対応していないブラウザにも対応可能です

Can I use
polyfill

使い方

//オプション指定
const options = {
  //要素の見えている割合が20%を超える度にコールバックを実行
  threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
}

//交差した時に発生するコールバック
const callback = (entries, observer) => {
  entries.forEach(entry => {
    //交差している領域の割合が20%を超えた場合
    if (entry.intersectionRatio > 0.2) {
      //アニメーションや画像の読み込みなどの処理
    }
  });
}

//IntersectionObserverをインスタンス化
const observer  = new IntersectionObserver(callback, options);

//監視する要素の配列を取得
const observers = [...document.querySelectorAll('.js-event')];

//配列に指定した要素をIntersectionObserverで監視
observers.forEach(el => {
  observer.observe(el);
});

基本的にはこれだけです。コールバックの処理を書く以外にはほとんど何も必要ありません。ただ細かく制御したい場合は以下のポイントを変更すればOKです。

オプション指定

//オプション指定
const options = {
  // ルートとして指定する要素。省略するとデフォルトはviewport
  root: document.querySelector('.root'),

  // 交差する上下左右100px手前で発火させることができる
  rootMargin: "100px",

  //要素の見えている割合が20%を超える度にコールバックを実行
  threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
}

rootについては基準にしたい要素を指定します。

rootMarginは交差する前にイベントを発生させたい場合に使います。画像の遅延読み込みなんかはこれを使うと画面に入ってくる直前に読み込みできるので便利かもしれませんね。

thresholdはイベントの発生するタイミングを調整できます。例のように配列で指定すると、指定した要素が画面に0%入った時、20%入った時、40
%入った時、60%入った時、80%入った時、100%入った時の計6回、交差している間にコールバックイベントを発生させます。
こちら例として配列を使いましたが、僕は基本的に複数回発生させる処理は不要なので、0.2とか0.5とか配列でなくそのまま数値で指定して1回だけ発生させています。

コールバック

//交差した時に発生するコールバック
const callback = (entries, observer) => {
  entries.forEach(entry => {
    //交差している領域の割合が20%を超えた場合
    if (entry.intersectionRatio > 0.2) {
      //アニメーションや画像の読み込みなどの処理
    }
  });
}

ここではentry.intersectionRatioという部分で、イベント発生した時に交差している割合を取得できるのでそれを使って処理を分岐しています。
他にもイベント発生時に以下のような情報を取得できます。

  • boundingClientRect:イベント発生した要素の座標位置
  • intersectionRect:交差領域の座標位置
  • isIntersecting:イベント発生時に交差しているかどうか
  • isVisible:イベント発生時に要素がすべて見えているかどうか(?)
  • rootBounds:ルート要素の座標情報
  • target:イベント発生した要素
  • time:タイムスタンプ

簡単なデモ

とりあえず簡単なデモを作ってみました。(IE対応はしていません)
DEMO

偶数の番号が書いてある要素が画面内に要素の20%が交差すると背景が黒に変わるアニメーションを付けてみました。

具体的にはコールバック関数に以下を追加しただけのシンプルな実装です。

if (entry.intersectionRatio > 0.2) {
   //クラスを追加
   entry.target.classList.add('is-effect');
}

クラスの付け替えでCSSアニメーションを発生させることができますね。ここではtransitionを使いましたが、もっと複雑な動きならanimationを使った方が良いかもしれません。

さいごに

Intersection Observer APIについてサクッとメモしました。
今までスクロールイベントの負荷で困っていた方や気になっていた方は有効なので使ってみてください。

記事一覧

RELATED

inoue
TIPS

【CSS + JS】テキストの無限ループアニメーション

inoue
TIPS

【Shopify】カートへの遷移を飛ばしてチェックアウトに進むボタンを設置する

inoue
TIPS

【JS】PhotoSwipe v5を使って画像をポップアップ表示する

inoue
TIPS

gulpでjpg・pngからAVIF画像を生成する

inoue
TIPS

【JS】MutationObserverでDOMを監視[PhotoSwipe(v5)+Swiper(v8)連携]

inoue
TIPS

【Google Sheets API】 スプレッドシートのデータをJSONで取得する

NEW POSTS

inoue
TRY

【WordPress】GA4連携の人気記事ランキング機能を自作プラグイン化してみた

inoue
TRY

【netlify】ビルドイメージを更新 [ Ubuntu Xenial 16.04 → Ubuntu Focal 20.04 ]

inoue
TIPS

【PHP】Google Business Profile APIを使ってクチコミを取得する

inoue
TRY

スクリーンショットのAPI「screendot」を使ってみた

inoue
TIPS

【window.matchMedia】メディアクエリでhoverが使えるデバイスを判定

inoue
TIPS

lax.jsの使い方【スクロール連動アニメーションの実装】

ブログ記事一覧
  • HOME
  • TIPS
  • Intersection Observerを使って画面内に要素が入ったらアニメーションさせる