スクロールアニメーションが作りやすくなる「Intersection Observer 」とは?

これまで「ウィンドウスクロール」によってアニメーションをする場合には「Javascript」での制御が主流な方法でした。

画面内に「要素があるか?」などを「Javascript」で検知して「アニメーション処理」を行う必要があるため、「アニメーション制御のプログラム」を書く必要があります。

「Intersection Observer」は「より簡潔にプログラムが作れる」ことと「処理の負荷が少ないこと」が特徴で、今後の「スクロールアニメーションの主流」になっていく可能性もあります。

目次

「Intersection Observer」とは?

「Intersection Observer」は、「要素の表示範囲」を管理してくれるAPIです。

ある要素の「●●%」が表示されたら「●●」を実行するということができます。

一番左は画像全体の20%の部分が表示された状態です。

中央は画像全体の50%の部分が表示された状態になります。

そして、一番左は画像の100%が表示された状態です。

「Intersection Observer」は、このように「画像の表示されている割合」を元に処理を実行できる仕組みです。

例えば、「不透明度」をそれぞれの表示状態によって変化させたり、画像自体を変形させたりとさまざまな表現や処理を行うことができます。

プログラムの書き方

今回作成したプログラムは、下記のようになります。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IntersectionObserver</title>
    <style>
        .io_code section {
            width: 100%;
            height: 100vh;
        }
    </style>
</head>

<body>
    <div class="io_code">
        <section></section>
        <div>表示割合:<span id="displayRate">0.0</span>%</div>
        <img src="photo_io.jpg" id="photo_io">
    </div>
    <script>
        //オプションの指定
        let options = {
            root: null,                //オブサーバーの対象となる境界を指定。「null」の場合はビューポートの境界。
            rootMargin: "0px",         //rootオプションで指定した境界にマージンを指定
            threshold: [.2, .5, 1.0]   //コールバック関数を実行する際の表示割合
        };

        //「threshold」で指定された割合に到達した時に実行するコールバック関数
        const callback = (entries, observer) => {
            entries.forEach((entry) => {
                document.getElementById("displayRate").innerHTML = parseInt(entry.intersectionRatio * 100);
            });
        }

        //「Intersection Observer」を生成
        let observer = new IntersectionObserver(callback, options);

        //表示割合を判定する要素
        let photo = document.querySelector("#photo_io");

        //「表示部分の変化」の監視を開始
        observer.observe(photo);
    </script>
 </body>

</html>

のようになります。

このページの下の方に画像があるので、スクロールしてみてください。

表示割合:0.0%

画像が見え始めた時に「画像の上にある表示割合」が変化しているのが見えると思います。

今回は、

let options = {
        root: null,                //オブサーバーの対象となる境界を指定。「null」の場合はビューポートの境界。
        rootMargin: "0px",         //rootオプションで指定した境界にマージンを指定
        threshold: [.2, .5, 1.0]   //コールバック関数を実行する際の表示割合
};

の「threshould」の値に「.2」を指定していますがこれは「0.2 = 20%」のことです。

同様に「.5」は「0.5 = 50%」で、「1.0」は「100%」のことです。

誤差が出ますが概ねこの割合で表示されているのが見えるかと思います。

この表示割合に達した時に、下記のコールバック関数が実行されます。

const callback = (entries, observer) => {
    entries.forEach((entry) => {
        document.getElementById("displayRate").innerHTML = parseInt(entry.intersectionRatio * 100);
    });
}

IDが「displayRate」の要素に、「現在の表示割合」を表示していますが、「entry.intersectionRatio」に表示割合が格納されています。

この値は「0(0%)~1(100%)」の範囲で変化するため、この値を利用してさまざまな表現を行うことができます。

補足ですが、「callback関数」の引数の「entries」にはIntersectionObserverEntryというオブジェクトです。

このオブジェクトの中に「オブジェクトの交差情報」が入っています。

もう1つの「引数」の「observer」には、「IntersectionObserver」のオブジェクトが入っています。

このオブジェクトは「要素の表示範囲(交差範囲)」を検知できる機能を持っています。

「observeメソッド」に「監視対象の要素」を渡すことで、「thresholdオプション」に指定した「表示割合」になると任意の処理を実行することができます。

「複数の要素」の設定方法

「複数の要素」の表示範囲を監視するためには下記のようにプログラムを作成します。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IntersectionObserver</title>
    <style>
        .io_code section {
            width: 100%;
            height: 100vh;
        }
    </style>
</head>

<body>
    <div class="io_code">
        <section></section>
        <div>表示割合:<span id="displayRate2">0.0</span>%</div>
        <img src="photo_io2.jpg" id="photo_io2">
        <div>表示割合:<span id="displayRate3">0.0</span>%</div>
        <img src="photo_io3.jpg" id="photo_io3">
        <div>表示割合:<span id="displayRate4">0.0</span>%</div>
        <img src="photo_io4.jpg" id="photo_io4">
    </div>
    <script>
        let options = {
            root: null,
            rootMargin: "0px",
            threshold: [.2, .5, 1.0]
        };

        const callback = (targetElementId) => (entries, observer) => {
            console.log(observer);
            entries.forEach((entry) => {
                document.getElementById(targetElementId).innerHTML = parseInt(entry.intersectionRatio * 100);
            });
        };

        const photos = ["#photo_io2", "#photo_io3", "#photo_io4"];

        for (let i = 2; i <= photos.length + 1; i++) {
            const observer = new IntersectionObserver(callback(`displayRate${i}`), options);
            observer.observe(document.querySelector(photos[i - 2]));
        }

    </script>
</body>

</html>

特に新しい内容はありませんが、「IntersectionObserver」をそれぞれの監視対象要素ごとに生成し、「observeメソッド」を実行しています。

表示状態は下記のようになります。

表示割合:0.0%
表示割合:0.0%
表示割合:0.0%

このようにさまざまな要素の「表示状態」によって処理を実行できる仕組みが「IntersectionObserver」です。

画面に要素が表示されるタイミングでさまざまなアニメーションを実行したりできます。

それではいくつか例を見ていきたいと思います。

「IntersectionObserverアニメーション」の事例

写真が「横スライドしながら表示されるアニメーション」を作ってみました。

アイデア次第でさまざまなWEBアニメーション表現ができると思いますので、スクロールと連動した「オリジナルWEBアニメーション」を作成してみてはいかがでしょうか。

この記事を書いた人

システム開発会社のWEBデザイン部で「デザイン&サイト構築」を担当しています。

目次