【JS】Intersection Observerでスクロール連動エフェクト

JavaScript
円周率π
円周率π

スクロールすると要素が変化するアニメーションっていいよね~

動点P
動点P

新しいIntersection Observerっていう交差監視 APIがあるんだけど、

使ってみない?

動点Qちゃん
動点Qちゃん

交差監視 APIでより快適にスクロールアニメーションが作れるのです・・・

動点P
動点P

Qちゃん、詳しく教えてよ!

Intersection Observerとは

Intersection Observer API (交差監視 API) は交差させたい要素を監視して、交差したときに動作させたいコールバック関数(後で処理する関数)を登録しておくことができます。なのでスクロールして、ある場所に来たら要素にアニメーションをつけたいときなどにぴったりな機能です。

要素がブラウザの表示領域に交わったかどうかを検知してくれるので、以前のようにgetBoundingClientRectで常にスクロール量を取得するという処理でパフォーマンスを低下させることもありません。また、ビューポート(見ているウィンドウ画面)が変化しても大丈夫。レスポンシブ対応です。

Intersection Observerを使って作ってみた試作です。真ん中の白い円が、灰色の背景部分から出たときに色がつきます。

See the Pen changecolor by inu (@tenp) on CodePen.

これを例にとり解説してみたいと思います。

Intersection Observer APIの設定の仕方

Intesection Observer APIはIntersectionObserver インスタンス(コピーして作ったオブジェクト)を生成して使います。第1引数には交差したときに実行する関数(コールバック関数:あとで作成します)を、第2引数にはオプション設定(これも後で設定します)のオブジェクトを渡します。

↓IntersectionObserverの機能を持ったオブジェクトを作ってobserverという変数名で定義したところ

const observer = new IntersectionObserver(callback, options);

順番が前後していますが、↓以下は検知させたい要素を取得しています。

const boxes = document.getElementsByClassName('box');
ポイント:要素を取得するメソッドし
  • getElementById()・・・Id名で取る。要素そのものが取れる
  • getElementsByClassName()・・・クラス名でとる。HTMLCollectionという配列のような何かがとれる
  • querySelector()・・・クラス名でとる。要素そのものが取れる
  • querySelectorAll()・・・クラス名でとる。NodeListという配列のような何かがどれる

オプション設定

オプション設定のオブジェクトは以下の設定が可能です。これらは要素がどの程度枠と交差したらアニメーションをつけるかを設定できるプロパティです。

const options = {
  root: null,
  rootMargin: '0px',
  threshold: 0.3
};

root・・・交差を検知する枠。デフォルト値のnullにするとビューポート(ウィンドウ画面そのもの)になる。ここにスクロールできる要素を入れると、その要素の中で監視ができます。

rootMargin・・・交差を検知する枠のマージン。”0px,15px,20px,36px”みたいに四方の値を設定できる。pxの単位は絶対つけなきゃダメ。

threshold・・・枠ではなく検知したい要素側につける閾値。0〜1の間で数値もしくは配列形式で設定する。デフォルト値は0。0.25なら要素が交差した割合が25%のときにコールバックが実行される。(つまり大体の場合そのときにアニメーションが始まるということ)[0.2,0.5,1]なら20%、50%、100%のタイミングで実行される。

オプション設定の図

監視対象を設定する

そして監視対象の要素を、さきほど作ったobserverに設定して監視させます。

for (let i = 0; i < boxes.length; i++) {
  // さっきのインスタンスにターゲットとなる要素を渡す
  observer.observe(boxes[i]);
}

このときgetElementsByClassName()などで要素を取得すると、取れるのはHTMLCollectionという配列のような何か、querySelectorAll()NodeListという配列のような何かが取れるので、forEachが使えないという問題(NodeListはIE以外ではforEach可)があります。そんなときは以下のように配列にしてからforEachで回すと良いらしいです。

const targets = Array.prototype.slice.call(targets);

targets.forEach(function(item) {
  // itemを利用した処理
});

for文ならわりとなんでも回せちゃうらしいので、自分はfor文でいいじゃないかと思うんですが、嫌がる方が多いみたいですね…書きかたが煩雑だから?でしょうか。

参考リンク

[JS]getElementByIdでは上手くいくのにgetElementsByClassNameでは上手くDOM操作が出来ないときーma-ya’s CREATE

コールバック関数(交差したとき実行させたい関数)を作成する

やっと本題の、「交差したときに実行させたい関数」を作成します。

コールバック関数は IntersectionObserverEntry オブジェクトのリスト(監視対象が全部入ったリスト)とオブザーバーを受け取ります

function callback(entries, observer) {
  for (let i=0;i<entries.length;i++) {
    if (entries[i].isIntersecting) {	
    document.getElementById("circle01").style.borderColor = "#fff"								
      }else{
    document.getElementById("circle01").style.borderColor = "gray"							
		  }
	 }
}

 entries・・・IntersectionObserverEntry オブジェクトのリスト(監視対象が全部入ったリスト)

isIntersecting・・・はrootとターゲットが交差していればtrue。否であればfalseを返す。これを if 文で制御しています。

intersectionRatio・・・ターゲット要素とルートが交差した比率を0 ~ 1の数値で取得する。if (entry.intersectionRatio > 0.7)なら、要素がルートと70%より交差した段階で処理が実行されます

基本的な構造

Intesection Observer APIの基本的な構造全体です。

//オプション
const options = {
  root: null, //検知する枠
  rootMargin: '0px', //検知する枠のマージン
  threshold: 0 //ターゲット要素がどれくらいの割合で表示されているか
}

//ターゲット要素をDOMで取得
const target = document.querySelector('.target');

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

//ターゲット要素の監視を開始
observer.observe(target);

//コールバック関数(後で実行させたい処理)
function callback(entries, observer) {
  entries.forEach(entry => {
    if(entry.isIntersecting) {
        //ターゲット要素がルート枠に交差した時の処理
    } else {
      //ターゲット要素がルート枠に交差していない時の処理
    }
  })
}

未対応ブラウザ

Intesection Observer APIはIE11では対応していませんが、その他は最新バージョンでしたらほぼ対応しています。IEへの対応はポリフィルを利用します。読み込ませるjavascriptファイルより先に読み込ませてください。

World Wide Web Consortiumで配布されているもの↓

<script src="PATH/TO/intersection-observer.js"></script>

Polyfill.ioのもの

	
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver%2CIntersectionObserverEntry"></script>

参考リンク

JSでのスクロール連動エフェクトにはIntersection Observerが便利ーICS MEDIA

コメント