JSのIntersection Observerを使ったスクロールアニメーションの作り方

WEB制作で「スクロールして要素が画面内に現れたらアニメーションする」という機能はとても頻繁に使います。

今回はスクロールアニメーションの作り方で、JavaScriptの「Intersection Observer API」を使った方法をご紹介します。

Intersection Observerについては様々な記事で解説があると思いますが、より簡単な理解の仕方と、実際にアニメーションを実装するまでを辿っていきます。

コピペでも使えるようにしておくので誰でも使えると思います。

スクロールアニメーションの今までの作り方と問題点

「スクロールして要素が画面内に現れたらアニメーションする」機能を作る場合、肝心なことは「要素が画面内に現れた」という判定をどのように作るかという部分です。

画面内に現れた判定さえ作れれば、アニメーションをCSSで作り、そのclassを要素に付与すればいいからです。

では「要素が画面内に現れた」という判定を今までどのように作っていたかというと次の2つがあります。

1.JSで画面サイズと要素までの距離を計算する。

この方法はライブラリなどを使わずに済みますが、scrollイベントを使う必要があるため、「スクロールする度に実行(発火)する」ことになり、パフォーマンスにも影響してきます。

また、ページを読み込んだ後にウィンドウサイズを変えたりすると要素までの距離が変わり再計算が必要になるなどの手間がかかります。

スクロールする度に何度も実行されてしまう

2.ライブラリを使う

画面内に現れた判定を付けるライブラリは色々ありますが、例えば「inview.js」があります。
「inview.js」を使うとonClickイベントなどと同じように「画面内に現れたら」というイベントハンドラを使うことができます。
しかしこれもライブラリを1つ読み込ませることが必要になり、ほんの一部分だけに使いたい場合ならもっとシンプルに済ませられれば理想的です。

Intersection Observer APIとは

Intersection Observerは日本語に訳すと「交差点の観察者」となり、オブジェクトが交差するのを監視する機能ということです。

今回の場合、「ウィンドウ」と「ページ内要素」の2つのオブジェクトが交差するのを監視することに使うことでスクロールアニメーションを実現していきます。

Intersection Observerを使う場合、ライブラリのように長いコードを入れる必要も無く、scrollイベントのように何度も発火することも無いためシンプルでパフォーマンスにも良い結果になります。

Intersection Observer APIを使ったスクロールアニメーションのサンプル

See the Pen intersection observer test by YusukeHatanaka (@yusuke_code) on CodePen.

Intersection Observer APIの使い方

大雑把に分類して4つのステップがあります。

  • 1.IntersectionObserverコンストラクターのオプション設定
  • 2.IntersectionObserverコンストラクターの呼び出し
  • 3.任意のオブジェクトを監視する
  • 4.交差したときに実行する関数の設定

1.IntersectionObserverコンストラクターのオプション設定

このオプションを一言で表すなら「交差判定ポイントの設定」です。

実際の記述はこちらです。

//オプションの設定
const options = {
 //rootは監視エリアの設定 nullにするウィンドウとなる
  root: null,
  //rootMarginは監視エリアに対して交差判定するポイントの設定
  rootMargin: "-10% 0",
 //threshold(しきい値)はオブジェクトがどこまで交差したら交差判定にするかの値
  threshold: 0
};

オプションには root / rootMargin / threshold の3つを設定します。

rootは監視エリアの設定で、個別エリアをidやclassなどで指定することもできますが、今回はスクロールアニメーションのことだけで考えるのでnullを設定しウィンドウを監視エリアとします。

rootMarginは交差を判定するエリア(ライン)の操作です。
今回は”-10% 0″と指定しているため、ウィンドウよりも上下に10%内側で交差判定がされます。

つまりオブジェクトが画面に少し(ウィンドウの10%分)入ってから交差判定がされます。

サンプルで画面上下にintersection lineという線を入れているので、そこを交差するときにアニメーションするのが判ると思います。

rootMarginを “10%”としたら交差監視エリアがウィンドウよりも上下左右に10%大きくなります。

rootMarginはCSSのmarginのように”10px 10px 10px 10px”といったショートハンド形式で指定ができます。

threshold(しきい値)は今回の場合アニメーションする要素に対する設定で、要素の何割が交差した時点で交差判定を出すかの値です。
0~1で設定ができ、0なら少しでも交差したら交差判定、1なら全部が交差しきって初めて交差判定です。
0.5なら要素の50%交差したら交差判定となります。

2.IntersectionObserverコンストラクターの呼び出し

IntersectionObserverコンストラクターの呼び出しは次の記述になり、
・第一引数には交差したときの実行関数
・第二引数には先ほど設定したオプションが入ります。

//IntersectionObserverコンストラクターの呼び出し
const observer = new IntersectionObserver(callback, options);

callbackのところに入る実行関数は後ほど設定します。

3.任意のオブジェクトを監視する

監視したいオブジェクト(今回はアニメーションしたいオブジェクト)の取得と、
そのオブジェクトに対して監視を実行します。

// class="anime"要素全てが監視対象
const items = document.querySelectorAll(".anime");

// それぞれのitemを監視する
items.forEach(item => {
  observer.observe(item);
});

取得したitemsを先ほど生成したobserverの.observeメソッドを使って監視します。

これでアニメーションさせたい要素(class=”anime”)を交差監視する状態になりました。

4.交差したときに実行する関数の設定

最後に交差したときに実行する関数の設定です。

今回は、交差が検知された要素に2つのclassが付与されアニメーションするようCSSで設定しています。
1.data-animationの値 = アニメーションの種類
2.animeted = これがアニメーションの起点

実際の記述は以下になります。

function IntersectFunction(elements) {
  // 交差検知をしたもののなかで、isIntersectingがtrue要素にだけclassを付与する
  elements.forEach(element => {
    if (element.isIntersecting) {
      //data-animationの値をclassとして付与(cssで設定したアニメーションの種類)
      element.target.classList.add(element.target.dataset.animation);
      //animatedをclassとして付与(アニメーションの起動)
      element.target.classList.add('animated');
    }
  });
}

html側でdata-animationの値を設定し、その値をそのままclassとして付与します。
このclassによってアニメーションの種類を変えられるようCSSで設定します。

animatedにはCSSのanimation-duration:が設定されているため、これが付与されるとアニメーションが始まります。

このアニメーションCSSの解説はこちらの記事にまとめています。

html css js すべてのソースコードを通しで解説

最低限のソースコードをここにまとめます。
コピペしてそのままでも使えますし、カスタマイズもしやすい状態になっていると思います。

■HTML

HTMLにはアニメーションさせたい要素にそれを表すclass(ここでは”anime”)と、data-animation属性の値にCSSでセットしたアニメーションのclassを入れておくだけです。

<div class="anime" data-animation="bounce"></div>

■CSS

CSSではアニメーションの動きの内容(種類)となるclassと、アニメーションが始まる起点となる animation-duration が設定されたclassを用意します。

最低限この2つがあればOKですが、アニメーションのcssの便利な設定方法はこちらの記事で解説をしています。

/*bounce というアニメーションの設定*/
@keyframes bounce {
  from,
  20%,
  53%,
  80%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  40%,
  43% {
    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    -webkit-transform: translate3d(0, -30px, 0);
    transform: translate3d(0, -30px, 0);
  }

  70% {
    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    -webkit-transform: translate3d(0, -15px, 0);
    transform: translate3d(0, -15px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, -4px, 0);
    transform: translate3d(0, -4px, 0);
  }
}

.bounce {
  -webkit-animation-name: bounce;
  animation-name: bounce;
  -webkit-transform-origin: center bottom;
  transform-origin: center bottom;
}

/*アニメーションが始まる起点となるclass */
.animated {
  -webkit-animation-duration: 2s;
  animation-duration: 2s;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
}

■JavaScript

JavaScriptでは先ほど解説したコードを通しで書いていきます。

このままコピペしても使えるようになっています。

// class="anime"要素全てが監視対象
const items = document.querySelectorAll(".anime");

const options = {
  root: null, // ビューポートはルート(ウィンドウ)
  rootMargin: "-10% 0px", // ビューポートより10%内側が交差判定ポイント
  threshold: 0 // 閾値(いきち)は0
};

const observer = new IntersectionObserver(IntersectFunction, options);

// それぞれのitemを監視する
items.forEach(item => {
  observer.observe(item);
});

function IntersectFunction(elements) {
  // 交差検知をしたもののなかで、isIntersectingがtrue要素にだけclassを付与する
  elements.forEach(element => {
    if (element.isIntersecting) {
      //data-animationの値をclassとして付与(cssで設定したアニメーションの種類)
      element.target.classList.add(element.target.dataset.animation);
      //animatedをclassとして付与(アニメーションの起動)
      element.target.classList.add('animated');
    }
  });
}

まとめと補足

以上がIntersection Observerを使ったスクロールアニメーションの作り方解説です。

非常に短い記述でよく使うスクロールアニメーションを実現でき、今までの作り方と比べてもメリットが多いので使う場面は沢山あると思います。

また、今回の内容はあくまでスクロールアニメーションだけに特化した内容ですので、Intersection Observer の機能や使い道としては他にもたくさんありますので調べてみてください。

この記事をシェアする

  • Twitterにシェア
  • facebookにシェア
  • LINEにシェア
  • POCKETにシェア