Scroll Trigger Animationの作り方|スクロール位置で要素をふわっと表示する基本実装【JavaScript】

Scroll Trigger Animationのアイキャッチ画像

スクロールに合わせて要素がふわっと現れる演出は、記事一覧、特徴紹介、料金表、FAQ など、いろいろなUIで使われます。
ただし、勢いで実装すると「毎フレーム scroll を監視して重くなる」「一度表示した要素が何度も切り替わって落ち着かない」といった問題が出やすいです。

この記事では、Scroll Trigger Animation の基本的な考え方を、初心者向けにできるだけシンプルに整理します。
今回は Intersection Observer を使って、要素が画面内に入ったタイミングでクラスを付け、自然に表示する方法を作ります。

この記事を読むと、次のことがわかります。

  • Scroll Trigger Animation の基本構造
  • scrollイベント連打ではなく、Intersection Observer を使う理由
  • fade / slide を組み合わせた見やすい表示演出の作り方
  • 複数要素を順番に見せるときの考え方

スクロール系アニメーションにはいくつか種類があります。 基本の「表示系アニメーション」については、 Scroll Reveal Animation でも解説しているので、 あわせて読むと理解しやすくなります。

1.Scroll Trigger Animation とは?

Scroll Trigger Animation は、スクロール位置をきっかけに要素の見た目を変える演出です。

たとえば次のような場面でよく使われます。

  • セクション見出しが下からふわっと出る
  • カード一覧がスクロールに合わせて順番に表示される
  • 画像とテキストが画面に入ったタイミングで現れる
  • CTAボタンや機能紹介を目立たせる

ページ読み込み直後に全部動くのではなく、ユーザーがそこまで読んだ瞬間に表示されるのがポイントです。
このおかげで、ページ全体が見やすくなり、内容の区切りも伝わりやすくなります。

2.よくある実装の問題

Scroll Trigger Animation は見た目は簡単そうですが、作り方によっては扱いづらくなります。

よくあるのは、scroll イベントで毎回位置を計算する方法です。

window.addEventListener('scroll', () => {
  const rect = element.getBoundingClientRect();

  if (rect.top < window.innerHeight) {
    element.classList.add('is-visible');
  }
});

この方法でも動きますが、要素数が増えると毎回判定が走りやすく、管理もしづらくなります。
また、表示済みかどうかの制御を足し始めると、コードが少しずつ複雑になります。

そこで今回は、表示判定をブラウザ側に任せやすい Intersection Observer を使います。

なお、スクロールイベントを毎フレーム処理するとパフォーマンスに影響が出やすいため、 フレームレートとパフォーマンス の考え方もあわせて押さえておくと理解が深まります。

3.今回の完成イメージ

今回作るのは、以下の流れです。

  1. 最初は要素を少し下にずらして透明にしておく
  2. 要素が画面内に入ったら is-visible クラスを付ける
  3. opacity と transform の transition で自然に表示する

アニメーション自体はシンプルですが、実案件でもかなり使いやすい形です。

まずはHTML

まずは表示対象のカードを並べます。
それぞれに js-scroll-item を付けて、JavaScript からまとめて監視できるようにします。

<section class="features">
  <h2 class="section-title js-scroll-item">Scroll Trigger Animation</h2>

  <div class="card-list">
    <article class="card js-scroll-item">
      <h3>Fast Setup</h3>
      <p>Intersection Observer を使って、画面に入った要素を自然に表示します。</p>
    </article>

    <article class="card js-scroll-item">
      <h3>Clean Motion</h3>
      <p>opacity と transform だけで、見やすく軽いアニメーションにします。</p>
    </article>

    <article class="card js-scroll-item">
      <h3>Reusable Pattern</h3>
      <p>クラスの付け替えだけなので、いろいろなセクションに再利用できます。</p>
    </article>
  </div>
</section>

次にCSS

初期状態では少し下にずらし、透明にしておきます。
表示タイミングで is-visible が付いたら、元の位置と不透明状態に戻します。

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  background: #0f1115;
  color: #f5f7fb;
}

.features {
  width: min(960px, calc(100% - 32px));
  margin: 0 auto;
  padding: 120px 0;
}

.section-title {
  margin: 0 0 32px;
  font-size: clamp(28px, 4vw, 48px);
  line-height: 1.2;
}

.card-list {
  display: grid;
  gap: 20px;
}

.card {
  padding: 24px;
  border-radius: 20px;
  background: #171b22;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.card h3 {
  margin: 0 0 12px;
  font-size: 20px;
}

.card p {
  margin: 0;
  color: rgba(255, 255, 255, 0.72);
  line-height: 1.7;
}

/* Scroll Trigger 初期状態 */
.js-scroll-item {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 0.7s ease,
    transform 0.7s ease;
  will-change: opacity, transform;
}

/* 表示後 */
.js-scroll-item.is-visible {
  opacity: 1;
  transform: translateY(0);
}

JavaScriptで表示タイミングを判定する

ここでは IntersectionObserver を使って、要素が画面内に入ったタイミングで is-visible を付けます。

const items = document.querySelectorAll('.js-scroll-item');

const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach((entry) => {
    if (!entry.isIntersecting) return;

    entry.target.classList.add('is-visible');
    obs.unobserve(entry.target);
  });
}, {
  threshold: 0.2
});

items.forEach((item) => {
  observer.observe(item);
});

この実装のポイント

threshold: 0.2 で少し見えたら発火する

threshold は、対象要素がどのくらい見えたら判定するかの基準です。

threshold: 0.2

この場合は、要素の20%くらいが見えたら発火します。
早めに出したいなら小さめ、しっかり見えてから出したいなら大きめにします。

②表示後に unobserve() している

一度表示したら、今回は再監視しなくてよいので外しています。

obs.unobserve(entry.target);

これを入れておくと、
「一回表示されたらそのまま」
という自然な見た目になりやすく、処理も整理しやすいです。

③動かしているのは opacity と transform

今回のアニメーションは次の2つだけです。

  • opacity
  • transform: translateY()

この2つは比較的扱いやすく、UIアニメーションの基本でもあります。
Scroll Trigger は凝りすぎるより、まずは軽くて読みやすい動きにするのが大事です。

今回のようなアニメーションの質は、イージングの設定でも大きく変わります。 詳しくは easing / cubic-bezier を参考にしてみてください。

4.少し発展:順番に出すと気持ちいい

カードが複数ある場合は、少しずつ遅延を付けると、より見やすくなります。
たとえばHTML側で style="transition-delay: 0.1s" のように指定してもよいです。

<article class="card js-scroll-item" style="transition-delay: 0.1s;">
  ...
</article>

<article class="card js-scroll-item" style="transition-delay: 0.2s;">
  ...
</article>

<article class="card js-scroll-item" style="transition-delay: 0.3s;">
  ...
</article>

このやり方なら、JavaScriptを増やさなくても、見た目だけ順番表示にできます。

ただし、遅延を大きくしすぎるとテンポが悪くなるので、
0.05秒〜0.15秒くらいの差から始めると扱いやすいです。

5.実務で意識したいこと

Scroll Trigger Animation は便利ですが、入れすぎると逆に読みにくくなります。
特に本文中の小さな要素すべてを動かすと、ページ全体が落ち着かなく見えることがあります。

意識したいのは次の3つです。

①重要なまとまりだけ動かす

見出し、カード群、画像+説明ブロックなど、
セクション単位で動かすと自然です。

②動きは控えめにする

Scroll Trigger は「驚かせる」より、
読み進めやすくする補助として使う方が相性がよいです。

③一度表示したらそのままにする

毎回スクロールで出たり消えたりすると、情報が安定しません。
記事ページや紹介ページでは、一度見せたらそのままが基本的に使いやすいです。

スクロールに関するアニメーションは他にもいくつかパターンがあります。 ページ全体の動きを制御するなら Scroll Snap Animation や、 奥行きを演出する Parallax Scroll Animation もおすすめです。

6.実験:Scroll Trigger Animation を観察してみよう

下のデモでは、要素が画面内に入ったときに is-visible が付与され、ふわっと表示されます。
表示位置やタイミングを少し変えるだけでも、見え方の印象が変わるので観察してみてください。

観察ポイント

  • どの位置で表示が始まると自然に見えるか
  • translateY の距離が大きすぎないか
  • fade と slide の組み合わせが読みやすさを邪魔していないか
  • カードを順番表示にしたとき、テンポが重くなっていないか

7.まとめ

Scroll Trigger Animation は、スクロールに応じて必要な情報を自然に見せるための基本パターンです。

今回のポイントは以下です。

  • 表示判定は IntersectionObserver を使う
  • 要素が見えたら is-visible を付ける
  • アニメーションは opacity と transform を中心にする
  • 一度表示したら unobserve() で外すと扱いやすい

まずは今回のような fade + slight slide の形を土台にすると、いろいろなUIへ展開しやすいです。
派手さよりも、読みやすさと使いやすさを崩さないことを意識すると、長く使える Scroll Trigger Animation になります。


Scroll Animationシリーズ

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA