scrollイベントより軽い!IntersectionObserverを初めて使ってみた
スクールのコーディングテスト対策で、スクロールアニメーションを実装しました。
「ページをスクロールすると要素がふわっと現れる」あの動き、どうやって作るんだろう?とずっと気になっていました。調べてみると IntersectionObserver というAPIを使うと実現できることがわかり、実装してみました。この記事ではその仕組みと実装方法をまとめます。
IntersectionObserver とは?
一言でいうと、要素が画面内に入ったかどうかを監視するAPIです。
スクロールイベント(scroll)を使う方法もありますが、スクロールのたびに処理が走るので負荷が高くなりがちです。IntersectionObserver はブラウザが効率よく監視してくれるので、パフォーマンスの面でも優れています。
scroll イベントとの違い
| scroll イベント | IntersectionObserver | |
|---|---|---|
| 処理のタイミング | スクロールのたびに毎回 | 要素が画面に入ったときだけ |
| パフォーマンス | 重くなりやすい | 軽い |
| コードの量 | 多め | シンプル |
実装したコード
HTML
アニメーションさせたい要素に js-fade クラスをつけます。
<div class="js-fade">
<p>スクロールしたら表示される要素</p>
</div>
<div class="js-fade">
<p>こちらも表示される要素</p>
</div>
CSS
最初は非表示にしておいて、is-visible クラスがついたら表示されるようにします。
/* 初期状態:非表示 */
.js-fade {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
/* is-visibleクラスがついたとき:表示 */
.js-fade.is-visible {
opacity: 1;
transform: translateY(0);
}
translateY(20px) で少し下にずらしておき、表示されるときに元の位置に戻ることで「ふわっと上がってくる」動きになります。
JavaScript
const fadeElements = document.querySelectorAll('.js-fade');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
}
});
});
fadeElements.forEach((el) => {
observer.observe(el);
});
コードの解説
① 監視する要素をまとめて取得する
const fadeElements = document.querySelectorAll('.js-fade');
querySelectorAll で js-fade クラスのついた要素をすべて取得します。
② IntersectionObserver を作る
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
}
});
});
entries は監視している要素の配列です。entry.isIntersecting が true のとき、つまり要素が画面内に入ったときに is-visible クラスを追加します。
③ 各要素を監視対象に登録する
fadeElements.forEach((el) => {
observer.observe(el);
});
取得した要素を1つずつ observe() に渡して監視をスタートします。
詰まったところ
CSS に transition を書く場所
最初、is-visible の方に transition を書いてしまい、アニメーションが動きませんでした。
transition は初期状態(.js-fade)の方に書くのが正しいです。クラスが切り替わる前から transition が設定されていないと、変化をアニメーションとして認識してくれません。
querySelectorAll の結果は配列ではない
querySelectorAll の戻り値は NodeList という配列に似たオブジェクトで、そのままでは forEach が使えない場合があります。ただし現代のブラウザでは NodeList でも forEach が使えるので今回はそのまま使っています。
まとめ
IntersectionObserverは要素が画面内に入ったタイミングを検知できるscrollイベントより軽くてシンプルに書けるtransitionは初期状態のクラスに書くisIntersectingがtrueのときにクラスを追加してアニメーションを発火させる