Webサイトでよく見かける、スクロールに合わせて画像やテキストがふわっと表示されるアニメーション。これはユーザーの注目を集め、サイト体験を向上させる効果があります。
以前はscrollイベントを使って実装されていましたが、Intersection Observer APIを使うことで、パフォーマンスに優れ、より簡単にスクロールアニメーションを実装できるようになりました。この記事では、このモダンな手法を解説します。
目次
Intersection Observer APIとは
Intersection Observer APIは、特定の要素(ターゲット)が、ビューポート(または他の祖先要素)と交差した(in viewに入った)かどうかを非同期で検知するためのJavaScriptのAPIです。
従来のscrollイベントのようにスクロールのたびに処理が走るのではなく、交差した瞬間だけコールバック関数が実行されるため、ブラウザへの負荷が圧倒的に小さいのが特徴です。
主な用途
- スクロールアニメーションの発火
- 画像の遅延読み込み(Lazy Loading)
- 無限スクロールの実装
実装手順
スクロールアニメーションの実装は、以下の3ステップで行います。
- HTML: アニメーション対象の要素を用意する。
- CSS: アニメーションの初期状態(非表示)と、発火後の状態(表示)を定義する。
- JavaScript: Intersection Observerを設定し、交差を検知してクラスを切り替える。
1. HTMLの準備
アニメーションさせたい要素に、目印となるクラスを付与します。ここではjs-fadeinというクラスを使います。
<section>
<h2>コンテンツタイトル</h2>
<div class="box js-fadein">
ここに、ふわっと表示させたいコンテンツが入ります。
</div>
<p class="text js-fadein">
テキストも同じようにアニメーションします。
</p>
</section>
2. CSSの定義
要素の「初期状態」と、アニメーション発火によって付与される「出現状態」をCSSで定義します。
要素の出現は、非表示の状態から透明度(opacity)と位置(transform)を変化させて実現します。
/* アニメーション共通設定 */
.js-fadein {
/* アニメーション時間と変化の仕方を指定 */
transition: opacity 1s, transform 1s;
}
/* 1. 初期状態(画面外・非表示の状態) */
.js-fadein {
opacity: 0; /* 透明 */
transform: translateY(30px); /* 30px下にずらしておく */
}
/* 2. 出現状態(交差検知後に付与するクラス) */
/* このクラスが付与されたらtransitionが作動してアニメーションする */
.is-visible {
opacity: 1; /* 表示 */
transform: translateY(0); /* 元の位置に戻る */
}
3. JavaScriptで監視とクラス切り替え
メインとなるJavaScriptの実装です。
// 1. 監視対象となる要素を全て取得する
const targets = document.querySelectorAll('.js-fadein');
// 2. Intersection Observerのオプションを設定する
const options = {
root: null, // ビューポートを基準とする (nullで指定)
rootMargin: '0px 0px -50px 0px', // ビューポートの下端から50px手前で発火
threshold: 0 // ターゲット要素が1ピクセルでも見えたら発火
};
// 3. Observerのインスタンスを作成する
// 'callback'は交差したときに実行される関数
const observer = new IntersectionObserver(callback, options);
// 4. コールバック関数を定義する
function callback(entries, observer) {
entries.forEach(entry => {
// 要素が交差したかどうかを判定
if (entry.isIntersecting) {
// 交差していたら、出現させるためのクラスを追加
entry.target.classList.add('is-visible');
// 一度アニメーションが発火したら、その要素の監視を終了
// これにより、スクロールを戻した時の再発火を防ぎ、パフォーマンスも向上する
observer.unobserve(entry.target);
}
});
}
// 5. 全ての監視対象要素をObserverに登録する
targets.forEach(target => {
observer.observe(target);
});
オプション設定のポイント
root: 監視の基準となる要素。nullを指定するとビューポート(画面表示領域)が基準になります。rootMargin: 基準要素(root)のマージン。ここでは'0px 0px -50px 0px'と指定しており、ビューポートの下端から50px手前に交差した時点でis-visibleクラスが付与されるようになります。アニメーションを少し早めに発火させたい場合に便利です。threshold: ターゲット要素が基準要素と交差する割合(0.0〜1.0)。0: 1ピクセルでも見えたら発火1: 要素の全体が見えたら発火
scrollイベントよりもIntersection Observer APIを使うべき理由
画面内に要素が入ったかどうかはaddEventListener('scroll', ...)でも座標を計算することで実装することは可能です。
しかし、様々な理由によりaddEventListenerよりもIntersection Observer APIを使った方が良いでしょう。
addEventListenerを使うよりも Intersection Observer API を使う方が優れている主な理由は、パフォーマンスと使いやすさの点にあります。
1. パフォーマンスの大幅な向上
scrollイベントリスナーとIntersection Observer APIの最も大きな違いは、処理が実行されるタイミングと頻度です。
scrollイベントリスナーの問題点
- 高頻度な処理: ユーザーがスクロールするたびに、ブラウザは数ミリ秒ごとに大量の
scrollイベントを発火します。 - 強制的な再計算: イベントハンドラ内で要素の位置(
getBoundingClientRect()など)を計算したり、表示/非表示をチェックしたりする処理を行うと、スクロールの度にブラウザにレイアウトの再計算(リフロー)を強制します。 - ブラウザ負荷: これらの高頻度で重い処理はCPUに大きな負荷をかけ、特にモバイルデバイスではスクロールがカクついたり(ジャンク)、バッテリーを消費したりする原因となります。
Intersection Observer APIの利点
- 非同期・低頻度な実行: Intersection Observerは、要素がビューポート(または指定された要素)と実際に交差したときのみ、非同期でコールバック関数を実行します。
- ブラウザ任せの監視: 要素の正確なピクセル位置の監視はブラウザの内部処理に任せられます。開発者が手動で
scrollイベントごとに位置を計算する必要がありません。 - メインスレッドのブロック回避: スクロール処理の負荷をメインスレッドから切り離すため、他の重要なJavaScriptやレンダリング処理のブロックを防ぎ、スムーズなユーザー体験を提供します。
2. 実装のシンプルさと簡潔さ
Intersection Observer APIは、特定のユースケース(要素の表示/非表示の検出)に特化しているため、コードが遥かにシンプルになります。
| 特徴 | addEventListener(‘scroll’, …) | Intersection Observer API |
| 監視のロジック | 自分でビューポートの高さと要素の絶対位置を計算し、「要素のトップがビューポートの下端よりも上か?」といった複雑な条件式を書く必要がある。 | thresholdやrootMarginというシンプルなオプションを設定するだけで、ブラウザが交差を判定してくれる。 |
| 監視終了 | アニメーションが完了しても、手動でリスナーを削除するか、フラグで処理を停止させる必要がある。 | observer.unobserve(target)を呼び出すだけで簡単に監視を終了できる。 |
要素の可視性を検出してアニメーションを発火させたり、画像を遅延ロードしたりといったタスクでは、scrollイベントよりもIntersection Observer APIを使用する方が、コードがクリーンになり、何よりもWebサイトのパフォーマンスを劇的に向上させることができます。
まとめ
Intersection Observer APIを使ったスクロールアニメーションの実装は、従来のscrollイベントを使う方法に比べて、コードがシンプルになり、パフォーマンスも優れているため、現代のWeb開発では標準的な手法です。
この手法を応用すれば、さまざまなタイミングやエフェクト(左からスライド、拡大など)で要素を出現させることができます。