【CSS】CSSアニメーションの作り方 スクロール・クリックで発動させる方法【Javascript】

ウェブデザインでは、画像やテキストのアニメーションを活用して、ユーザーの目を引くことが重要です。

前回の記事では、CSSを使用したアニメーションの実装方法を紹介しました。

特定のタイミングでアニメーションを発動させるにはJavaScriptが必要です。(ChromeとEdge等のChromiumベースのブラウザにはスクロール駆動型アニメーションの実装があるため、JavaScriptなしでも実現できますが、それについてはまた別の記事で詳しく解説しようと思います。)

今回は、CSSでスクロール時やクリック時にアニメーションを実行する方法を紹介します。

要素が表示されたらアニメーションを開始(フェードイン)

CSSだけではページ読み込み時にアニメーションが開始されるため、スクロールしたときに要素が画面に現れたタイミングでアニメーションを適用するにはJavaScriptを使用します。

まずは、以下のDEMOをご覧ください。

スクロールによって要素が表示されると、それぞれのアニメーションが発動します。

Intersection Observerとは?

Intersection Observer は、要素がビューポート(画面内)に入ったかどうかを検知するためのJavaScript APIです。

scrollとは違いスクロールイベントを頻繁に監視しませんので、パフォーマンスがよくアニメーションのトリガーとして利用できます。

こちらがIntersection Observerを利用したコードとなっています。

<script>
    // Intersection Observer を作成
    const observer = new IntersectionObserver(
      // スクロール時に要素が画面内に入ったらアニメーションを適用
      entries => {
        entries.forEach(entry => {
            // 要素が画面内(ビューポート内)に入ったかどうかを判定
            if (entry.isIntersecting) {
                // .show クラスを追加してアニメーションを開始
                entry.target.classList.add('show');
            }
        });
      }, { threshold: 0.5 }); // 50%以上表示されたら発火

    // .animate クラスを持つすべての要素を監視対象に追加
    document.querySelectorAll('.animate').forEach(el => observer.observe(el));
</script>

entriesIntersection Observer API から渡される 配列 で、監視対象のすべての要素に関する情報が格納されています。

entry は、その 配列の各要素(個々の要素の情報) です。

ここでは、document.querySelectorAll('.animate') で取得した各HTML要素の情報(タグや位置、可視状態など)が入っています。

// entries
[
 // entry
 {
    target: <div class="animate">要素1</div>,  // 実際のHTML要素
    isIntersecting: true, // 画面内に入っているかどうか
    intersectionRatio: 0.8, // どれくらい表示されているか(0.0〜1.0)
    boundingClientRect: { ... }, // 要素の位置とサイズ
    rootBounds: { ... }, // ビューポート(画面)の位置とサイズ
    time: 3456.78 // イベント発生時間
  },
  // entry
  {
    target: <img class="animate" src="image.jpg" alt="要素3">,
    isIntersecting: false,  // まだ入っていない
    intersectionRatio: 0.0,
    boundingClientRect: { ... },
    rootBounds: { ... },
    time: 3456.78
  }
]

HTMLとCSS(参考のため余計な箇所は除いています)

<div class="container">
   <div class="box animate fade-in">
     <div class="label">フェードイン</div>
       <div class="image-box"><img src="sample.png"></div>
    </div>
   <div class="box animate zoom-in">
     <div class="label">ズームイン</div>
       <div class="image-box"><img src="sample.png"></div>
    </div>
   <div class="box animate slide-in">
     <div class="label">スライドイン</div>
       <div class="image-box"><img src="sample.png"></div>
    </div>
   <div class="box animate rotate">
     <div class="label">回転</div>
       <div class="image-box"><img src="sample.png"></div>
    </div>
</div>
/* --------------------------
【初期状態】 アニメーションを適用する要素
 -------------------------- */
.animate {
    opacity: 0; /* 初期状態では非表示 */
    transition: opacity 3s ease-out, transform 3s ease-out; /* 透明度と変形を3秒かけてスムーズに変化 */
}

/* フェードインアニメーション(下から上に移動しながら表示) */
.fade-in {
    transform: translateY(120px); /* 初期状態では下方向に120pxずらす */
}

/* ズームインアニメーション(拡大しながら表示) */
.zoom-in {
    transform: scale(0.1); /* 初期状態では10%のサイズ */
}

/* スライドインアニメーション(右から左へ移動しながら表示) */
.slide-in {
    transform: translateX(150px); /* 初期状態では右に150pxずらす */
}

/* 3D回転アニメーション(Y軸を中心に回転しながら表示) */
.rotate {
    transform: rotateY(90deg); /* 初期状態ではY軸方向に90度回転 */
}

/* --------------------------
 【最終状態】要素が画面内に入ったとき(アニメーション完了時)
 -------------------------- */
.show {
    opacity: 1; /* 完全に表示 */
    transform: translateY(0) scale(1) translateX(0) rotateY(0); /* 元の位置・サイズ・回転に戻す */
}

初期状態】各要素を非表示にしてずらす

  • .animate クラスが適用された要素は opacity: 0 なので 最初は非表示
  • さらに transform を適用し、各アニメーションごとに 位置やサイズ、回転を変更
    • .fade-in → 120px下にずらしておく
    • .zoom-in → 10%のサイズに縮小しておく
    • .slide-in → 右側に150pxずらしておく
    • .rotate → Y軸に90度回転しておく

【最終状態】(フェードイン)スクロールして要素が画面内に表示で戻す

  • Intersection Observer によって、要素が 画面の50%以上表示されたら show クラスを追加
  • .show クラスが追加されると、opacity: 1 になり transform がリセットされて 元の位置・サイズ・回転に戻る
  • transition: opacity 3s ease-out, transform 3s ease-out; によって、 3秒かけて戻していく

といった流れになります。

動きを調整したい時は、ずらす位置(transform)や、戻す時間(transition)で調整します。

続いてscrollイベントの例も紹介します。

豊富なオリジナルブロックでLPをかんたんに作成できるLP Creator

LPをかんたんに作成できるLP Creator

デザイン・機能・SEO・収益化にこだわったメディア運営者向け「STREETIST」

デザイン・機能・速度・SEO・収益化にこだわった、ブロガー・メディア運営者向けのデザインテーマ STREETIST

スクロールで色が変わっていく

スクロールに応じて要素の背景色が徐々に変化していくアニメーション。

次はscrollイベントを利用して作成します。まずはDemoを御覧ください。

Scrollイベントとは?

Intersection Observer は、要素がビューポート(画面内)に入った段階で検知(発火)するのに対して、スクロール (scroll) イベントとは、ユーザーがページをスクロールしたときに発火するイベントです。※スクロールする度にイベントが発火します。

これを使うことで、スクロール量に応じて 背景色を変えたり、要素をアニメーションさせたり できます。

シンプルな scroll イベントの例

scroll イベントを window に追加することで、スクロール時に処理を実行できます。

ユーザーがスクロールするたびに「スクロールされました!」がコンソールに表示
// scrollイベントをwindowに追加
window.addEventListener('scroll', () => {
    console.log("スクロールされました!");
});
スクロール量を取得して100px になったら処理を実行
window.addEventListener('scroll', () => {
    if (window.scrollY > 100) {
        console.log("100px以上スクロールしました!");
    }
});

DEMOの解説

このスクリプトでは、スクロールに応じてページの背景色が変化 するようになっています。
ページのスクロール量を取得し、それに応じて RGBの色を補間しながら変更 します。

body, html {
 margin: 0;
 padding: 0;
 height: 300vh; 
 background-color: rgb(240, 160, 200); /* 初期色を適用 */
}
<script>
    // スクロールイベントを追加(ページをスクロールすると発火)
    window.addEventListener('scroll', () => {

        // スクロール可能な最大の長さ(ページ全体の高さ - 画面の高さ)
        const maxScroll = document.documentElement.scrollHeight - window.innerHeight;

        // 現在のスクロール量を ユーザーが スクロール可能な高さ(px)割合 0.0 ~ 1.0 の範囲で取得
        const scrollPercent = window.scrollY / maxScroll;

        // スクロール量に応じてRGBの値を変更(赤 → 緑 → 青)
        const red = Math.floor(240 - (scrollPercent * 150));  // 240 → 90(赤を減少)
        const green = Math.floor(160 + (scrollPercent * 60)); // 160 → 220(緑を増加)
        const blue = Math.floor(200 + (scrollPercent * 40));  // 200 → 240(青を増加)

        // 計算した RGB の値を背景色に適用
        document.body.style.backgroundColor = `rgb(${red}, ${green}, ${blue})`;
    });
</script>

スクロール可能な最大範囲を計算
const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
  • document.documentElement.scrollHeight → ページ全体の高さ
  • window.innerHeight 現在の画面の高さ
  • 最大スクロール可能な範囲 を求めることで、どのくらいスクロールできるか を取得。

)ページの高さ: 3000px 画面の高さ: 800px
  maxScroll = 3000 – 800 = 2200px(スクロールできる最大値)

スクロールの進行度(0.0 ~ 1.0)を取得
const scrollPercent = window.scrollY / maxScroll;
  • window.scrollY現在のスクロール位置(px単位)
  • maxScroll で割ることで、スクロールの進行度を 0.0(最上部)~ 1.0(最下部) の範囲で取得。

(例)スクロール位置と進行度

スクロール位置 (window.scrollY)進行度(scrollPercent)
0px(最上部)0.0
1100px(中央付近)0.5
2200px(最下部)1.0

RGB値の計算
const red = Math.floor(240 - (scrollPercent * 150));  // 赤成分を減少(240 → 90)
const green = Math.floor(160 + (scrollPercent * 60)); // 緑成分を増加(160 → 220)
const blue = Math.floor(200 + (scrollPercent * 40));  // 青成分を増加(200 → 240)

// 計算した RGB の値を背景色に適用
document.body.style.backgroundColor = `rgb(${red}, ${green}, ${blue})`;

これでスクロール量 (scrollPercent) に応じて、RGBの各値を変化させbackgroudに適用させています。

スクロールで画像を大きくして、最後にClickイベントでアニメーション

最後に応用編として、スクロールに応じて要素を大きくしていき、ある程度大きくなったところで薄くする、最後にクリックされたら回転して消える。

イベントとアニメーションをいくつか実装したものです。

まずはDemoを御覧ください。

DEMO解説

スクロールに応じて画像が 拡大(scale(0)scale(1.0) ) し、
最大サイズになったら 徐々に透明度が低下(opacity: 1opacity: 0.3) する。
最後に「いかがでしたか?」の文字が フェードイン するアニメーションを実装し、クリックイベントを有効化します。

クリックされたら回転して右斜上に消えていきます。(disappear

<html>
<body>

<div class="image-container"><img src="sample.png"></div>

<div class="text-overlay">いかがでしたか?</div>

</body>
</html>
/* 画像コンテナの初期状態(スクロール前) */
.image-container {
    position: fixed; /* 画面に固定 */
    top: 50%; /* 要素の角を画面の中央に */
    left: 50%;
    /* 要素分半分ずらして真ん中に、初期状態は小さく縮小→大きく拡大していく */
    transform: translate(-50%, -50%) scale(0); 
    opacity: 1; /* 初期状態は完全に表示→最終状態で薄くする */
    transition: transform 0.1s linear, opacity 0.5s ease-out; /* 拡大と透明度変化を滑らかに */
}

/* 画像サイズ設定 */
.image-container img {
    width: 300px; /* 最大サイズ */
    height: auto;
    pointer-events: none; /* 初期状態ではクリック不可 */
}

/* テキストの初期状態(非表示) */
.text-overlay {
    position: fixed; /* 画面に固定 */
    top: 50%; /* 画面の中央 */
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: 0; /* 初期状態では見えない →最終状態で見えるように*/
    transition: opacity 0.5s ease-out; /* フェードインを滑らかに */
    font-size: 24px;
    font-weight: bold;
    color: black;
}

/* クリックイベントのアニメーション(回転しながら右斜め上へ飛び、小さくなり消える) */
.disappear {
    /* translateで200px右上に移動、scale(0)で縮小して消える、rotate(720deg)で2回転する */
    transform: translate(200px, -200px) scale(0) rotate(720deg);

    /* 完全に非表示になる */
    opacity: 0;

    /* transformとopacity で定義された動作を1秒かけて、スムーズに変化させる */
    transition: transform 1s ease-out, opacity 1s ease-out;
}
<script>
// スクロールイベントを追加(ページをスクロールすると発火)
window.addEventListener('scroll', () => {

    // ------------------------------------
    // スクロール位置と計算
    // ------------------------------------
    // スクロール可能な最大の長さ(ページ全体の高さ - 画面の高さ)
    const maxScroll = document.documentElement.scrollHeight - window.innerHeight;

    // 現在のスクロール量を 0.0 ~ 1.0 の範囲で取得
    const scrollPercent = window.scrollY / maxScroll;

    // ------------------------------------
    // 画像の拡大と透明度
    // ------------------------------------
    // 画像の拡大(0 → 1.0)
    // スクロールが進むほど scale が増加し、最大で 1.0(等倍)まで拡大
    const scale = Math.min(scrollPercent * 2, 1);

    // 透明度を減少させる(最大サイズの50%を超えたら透明化開始)
    const fadeStart = 0.5; // 50% スクロール時にフェード開始
    const opacity = scrollPercent < fadeStart ? 1  // 50% より前なら透明度は 1(完全に表示)
        : Math.max(1 - (scrollPercent - fadeStart) * 1.8, 0.1); // 最低 0.1 まで薄くする

    // ------------------------------------
    // 画像の拡大と透明度変更を適用
    // ------------------------------------
    document.querySelector('.image-container').style.transform = `translate(-50%, -50%) scale(${scale})`;
    document.querySelector('.image-container').style.opacity = opacity;

    // ------------------------------------
    // テキストのフェードイン(スクロールの60%以降)
    // ------------------------------------
    // 60% を超えると opacity が増加し、徐々に表示
    const textOpacity = Math.max(scrollPercent - 0.6, 0) * 2;
    document.querySelector('.text-overlay').style.opacity = textOpacity;

    // ------------------------------------
    // スクロールが100%(最下部)に達したらクリックを有効化
    // ------------------------------------
    if (scrollPercent >= 1) {
        document.querySelector('.image-container img').style.pointerEvents = "auto";
    } 

});

// ------------------------------------
// 画像クリックイベントの追加
// ------------------------------------
document.querySelector('.image-container img').addEventListener('click', () => {
    document.querySelector('.image-container img').classList.add('disappear');
});

</script>

まとめ

今回の記事では、CSSでアニメーションを定義し、JavaScriptを使って起動タイミングに応じたアニメーションを作成する方法 を解説しました。

方法概要
CSS @keyframesページロード時からループアニメーション
CSS transform要素の位置・回転・拡大縮小を変化させる
CSS opacity要素の透明度
CSS transition指定した要素の変化をスムーズにする
JavaScript Intersection Observer要素が画面内に入ったらアニメーション
JavaScript scroll イベントスクロール量に応じてアニメーション
JavaScript click イベントクリック時にアニメーション


特に以下のポイントを押さえることで、動的なアニメーションを効率よく作ることができます

アニメーションの基本は「初期状態 → 最終状態」を考える
  • 初期状態(画面ロード時の要素の位置、サイズ、透明度を設定)
  • 最終状態を定義(CSSの transitiontransform を適用)
アニメーションの発火方法を選ぶ
  • Intersection Observer → スクロールして要素が表示されたときに実行
  • scroll イベント → スクロール量に応じた動きを作る
  • click イベント → ユーザーのアクションに応じた動きを作る
  • 負荷を考える場合は、Intersection Observerをできるだけ使いましょう

どうでしょうか?

いろいろなアニメーションが、意外と簡単に作成できます。
普段はライブラリを利用している方も多いと思いますが、基本を理解すれば、これらを応用してより魅力的なアニメーションを自在に作ることができます。

ぜひ試してみてください!

(PR)LPをかんたんに作成できるLP Creator