フロントエンド 2026.05.08

スクロールで伝わる!Intersection Observer APIアニメーション入門

約12分で読めます

スクロール連動アニメーションをIntersection Observer APIで実装する方法を解説。実案件での活用例や失敗パターンも含めた実践的な入門記事です。

「なんかサイトが地味に見える」という悩み、心当たりありませんか?

ページのデザインはきれいに整えた。コンテンツも充実している。なのに、いざ公開してみると「なんとなく古臭い」「競合他社のサイトのほうが動きがあってカッコいい」と感じたことはないでしょうか。

あるいは、スクロールに合わせてふわっと要素が浮かび上がるあのアニメーション、実装したいのに何から手をつければいいかわからない――そんな状況にある方にこそ、この記事を読んでいただきたいと思います。

今回紹介する Intersection Observer API を使えば、jQueryへの依存もなく、パフォーマンスへの悪影響も最小限に抑えながら、スクロール連動アニメーションをスマートに実装できます。コード量も驚くほど少なく済みます。


なぜ「スクロールイベント」では限界があるのか

従来、スクロール連動アニメーションを実装しようとすると、多くのエンジニアが window.addEventListener('scroll', ...) を使ったアプローチを選んできました。しかしこの方法には、見落としがちな問題が潜んでいます。

scroll イベントはページをスクロールするたびに毎フレーム何十回も発火します。その都度 getBoundingClientRect() で要素の位置を計算させると、ブラウザが強制的にレイアウトを再計算(リフロー)し、描画が詰まってしまうのです。結果として、スクロールがカクつく、バッテリーの消耗が激しいといった体験の劣化につながります。

Intersection Observer APIはブラウザのレンダリングエンジンが非同期で要素の交差を監視してくれます。開発者がポーリングする必要がなく、要素がビューポートに入ったタイミングだけコールバックが走る仕組みです。モダンブラウザ全対応済みで、実務投入に迷う理由はほとんどありません。


フロントエンド開発をお探しですか?

React・Vue・モダンなUI/UX開発をサポートします

無料で相談する

実際の案件から学ぶ:スクロールアニメーションの実装手順

神奈川県内の製造業クライアントからコーポレートサイトのリニューアルを受注した際、「会社の強みをスクロールしながら自然に伝えたい」というオーダーをいただきました。テキストや画像が一斉に表示されるのではなく、スクロールに合わせて順番にフェードインしてくる演出を希望されていたのです。

そこで採用したのがIntersection Observer APIです。以下にそのコアとなる実装を紹介します。

Step 1: HTMLの準備

アニメーションさせたい要素に js-fade クラスを付与するだけです。

<section class="feature">
  <div class="js-fade">
    <h2>品質へのこだわり</h2>
    <p>創業以来、一切妥協しない製造工程を守り続けています。</p>
  </div>
  <div class="js-fade">
    <h2>圧倒的な納期対応力</h2>
    <p>緊急案件にも柔軟に対応できる体制を整えています。</p>
  </div>
</section>

Step 2: CSSでアニメーションの初期状態を定義

JavaScriptが実行される前の状態(非表示・少し下にずれた状態)をCSSで定義し、クラスが付与されたタイミングで動きを加えます。

.js-fade {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.js-fade.is-visible {
  opacity: 1;
  transform: translateY(0);
}

transition でアニメーションの速さと緩急を制御しています。ease は自然な動きを出すのに最適で、機械的な印象を与えません。

Step 3: JavaScriptでIntersection Observerを設定

// 監視対象の要素をすべて取得
const targets = document.querySelectorAll('.js-fade');

// Observerのオプション設定
const options = {
  root: null,         // ビューポートを基準にする
  rootMargin: '0px',  // マージンなし
  threshold: 0.2      // 要素の20%が見えたらコールバック発火
};

// コールバック関数
const callback = (entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      // 一度発火したら監視を解除(パフォーマンス最適化)
      observer.unobserve(entry.target);
    }
  });
};

// Observerのインスタンスを生成して監視開始
const observer = new IntersectionObserver(callback, options);
targets.forEach((target) => observer.observe(target));

threshold: 0.2 は要素の20%がビューポートに入った時点で発火します。0に設定すると要素のほんの端が見えただけで動くため、体感が急ぎすぎる印象になります。0.2〜0.3程度が多くのケースで自然な塩梅です。

応用:複数要素を時間差でアニメーションさせる

上記の製造業案件では、カード要素を時間差でフェードインさせることで、情報が「積み上がっていく」ような演出を実現しました。

const callback = (entries) => {
  entries.forEach((entry, index) => {
    if (entry.isIntersecting) {
      // indexを使って遅延時間を計算
      setTimeout(() => {
        entry.target.classList.add('is-visible');
      }, index * 150); // 150ms刻みで順番に表示
      observer.unobserve(entry.target);
    }
  });
};

この改修後、クライアントから「スクロールするたびにページが生きているみたいで、来社した取引先にも好評だった」というフィードバックをいただきました。

flowchart TD
    A[ページ読み込み完了] --> B[IntersectionObserver 初期化]
    B --> C[.js-fade 要素を監視登録]
    C --> D{ユーザーがスクロール}
    D --> E{要素がビューポートに\n20%以上入ったか?}
    E -->|No| D
    E -->|Yes| F[is-visible クラスを付与]
    F --> G[CSSアニメーション発火]
    G --> H[unobserve で監視解除]

よくある失敗パターンと対処法

実案件を通じて遭遇してきた、やりがちなミスをまとめます。

❌ 失敗1:unobserve を忘れる

一度表示した要素でも監視し続けると、スクロールの往復のたびにクラスが着脱されてアニメーションが繰り返し再生されます。「一度見せたら終わり」という用途であれば、必ず observer.unobserve(entry.target) を入れましょう。逆に、スクロールバックで消して再表示させたい場合は意図的に外しておく必要があります。

❌ 失敗2:threshold を高くしすぎる

threshold: 1.0 にすると「要素の100%がビューポート内に入ったとき」が発火タイミングになります。背の高い要素(縦長のセクションなど)では、スクロールしきっても条件を満たさず永遠に発火しないという事態が起こります。モバイルでは特に注意が必要です。

❌ 失敗3:CSSの transition を忘れる

JavaScriptでクラスを付与してもCSSに transition がなければ、アニメーションではなくパッと切り替わる瞬間移動になります。フェードの動きはあくまでCSSが担っていることを意識しておきましょう。

❌ 失敗4:prefers-reduced-motion を無視する

アニメーションが不快・有害に感じるユーザーへの配慮も必要です。OSの設定で「視差効果を減らす」を有効にしているユーザーに対して、アニメーションを止めるCSSを必ず追記してください。

@media (prefers-reduced-motion: reduce) {
  .js-fade {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

この一行があるかないかで、アクセシビリティへの意識が問われます。

基本は0.1〜0.3の範囲で調整してください。カードや短いテキストブロックなら0.2が自然です。ファーストビュー直下の大きなセクションでは0.1に下げると早めに反応してスムーズに見えます。実際の画面でスクロールしながら感触を確かめるのが最短ルートです。
モバイルでは要素の高さがビューポートを超えることがよくあります。`threshold`を高く設定している場合は0.1程度に下げてください。また、`rootMargin`に負の値(例: `"-50px"`)を設定すると逆に発火が遅れるため、モバイルでは`0px`にしておくのが安全です。
使えます。Vueなら`mounted()`フック内、Next.jsなら`useEffect()`内でObserverを初期化してください。コンポーネントのアンマウント時に`observer.disconnect()`でメモリリークを防ぐことも忘れずに。

フロントエンド開発をお探しですか?

React・Vue・モダンなUI/UX開発をサポートします

無料で相談する

Web制作・UI改善もお任せください

フロントエンド開発

モダンな技術で、使いやすく美しいWebサイトを実現します

200件以上の制作実績 顧客満足度97% 初回相談無料

※ 通常1営業日以内にご返信します

まとめ:まず手を動かすための3ステップ

Intersection Observer APIは学習コストが低く、導入効果が目に見えやすい技術です。適切に実装すれば、訪問者の「もっと読みたい」という気持ちを自然に引き出し、ページ全体の印象を底上げします。

難しく考える必要はありません。まずは既存のサイトの1セクションに試してみることから始めてください。

「実装はわかったけれど、自サイトへの組み込みや既存デザインとの整合性が不安」という場合は、ぜひFivenine Designにご相談ください。20年以上の実績をもとに、貴社のサイトに最適な形でご提案します。

この記事をシェア

Webサイトの改善、お任せください

デザイン改善・表示速度向上・レスポンシブ対応など、成果の出るWeb制作を行います。 初回相談は無料です。

※ 1営業日以内にご返信いたします

この技術でお困りなら

無料でプロに相談できます

相談する
AIに無料相談