【jQuery】画像を1枚ずつフェードインする完全ガイド|順番表示・スクロール連動・ループ・ギャラリーまで

ページを開いたときに画像が1枚ずつ順番にフワッと現れるアニメーションは、ポートフォリオや商品ギャラリーでよく使われる演出です。jQueryの animate()delay() を使えば、数行のコードで実装できます。

この記事でわかること

  • 再帰関数で画像を1枚ずつ順番にフェードインする基本実装
  • delay()を使ったシンプルな遅延フェードイン
  • スクロールして画面内に入ったら順番にフェードインする方法
  • 最後の画像の後にループ再生する実装
  • クリックで次の画像を表示するスライドショー風の実装
  • グリッドギャラリーへの応用(複数列・千鳥遅延)
  • animate()とCSSトランジションの使い分け
スポンサーリンク

1. 基本実装:再帰関数で1枚ずつ順番にフェードイン

前の画像のフェードイン完了を待ってから次の画像を開始するには、再帰関数(コールバック内で自分自身を呼び出す)を使います。

HTML・CSS

<!-- 画像は最初は非表示にしておく(CSSで opacity:0 を設定) -->
<div id="img-container">
  <img class="seq-img" src="photo1.jpg" alt="写真1">
  <img class="seq-img" src="photo2.jpg" alt="写真2">
  <img class="seq-img" src="photo3.jpg" alt="写真3">
</div>
.seq-img {
  opacity: 0;       /* 最初は非表示 */
  display: block;
  margin-bottom: 16px;
}

jQuery

$(function () {
  var $imgs = $('#img-container .seq-img');

  function fadeNext(index) {
    if (index >= $imgs.length) return;  // 全画像が終わったら停止

    $($imgs[index]).animate({ opacity: 1 }, 600, function () {
      fadeNext(index + 1);  // フェードイン完了後に次を開始
    });
  }

  fadeNext(0);  // 0枚目から開始
});
コールバックで「完了後に次へ」を実現
animate() の第3引数はアニメーション完了後に呼ばれるコールバック関数です。コールバックの中で次のインデックスで fadeNext() を呼ぶことで、1枚ずつ順番に表示されます。fadeIn(600, callback) を使っても同じ動作になります。アニメーション全般の詳細はアニメーション完全ガイドを参照してください。

2. delay()を使ったシンプルな遅延フェードイン

前の画像の完了を厳密に待つ必要がない場合は、delay() で固定の遅延時間を設定する方がシンプルです。

$(function () {
  var INTERVAL = 400;  // 1枚ごとの遅延(ms)
  var DURATION = 600;  // フェードインの時間(ms)

  $('.seq-img').each(function (i) {
    $(this)
      .delay(i * INTERVAL)          // 0ms / 400ms / 800ms ... と遅延
      .animate({ opacity: 1 }, DURATION);
  });
});
再帰関数 vs delay() の使い分け

  • 再帰関数: 前の画像が完全に表示されてから次を開始。遅延時間がアニメーション時間に依存しない
  • delay(): 固定間隔で次々に開始するため、アニメーションが重なる場合がある。コードがシンプル

ふんわりとしたギャラリー演出なら delay()、スライドショー的に1枚ずつ厳密に制御したいなら再帰関数が向いています。

3. スクロールして画面内に入ったら順番にフェードイン

ページ読み込み時ではなく、スクロールして画像が見えた瞬間にフェードインするパターンです。長いページのギャラリーに適しています。

$(function () {
  var DURATION = 600;
  var STAGGER  = 150;  // 千鳥遅延(同時に見えた複数枚をずらす)

  function checkVisible() {
    var scrollTop    = $(window).scrollTop();
    var windowBottom = scrollTop + $(window).height();

    $('.scroll-img:not(.revealed)').each(function (i) {
      var elemTop = $(this).offset().top;

      if (elemTop < windowBottom - 40) {  // 40pxの余裕を持って判定
        var $el = $(this);
        setTimeout(function () {
          $el.addClass('revealed').animate({ opacity: 1 }, DURATION);
        }, i * STAGGER);  // 複数枚が同時に入った場合は千鳥で遅延
      }
    });
  }

  $(window).on('scroll', checkVisible);
  checkVisible();  // ページ読み込み時も初回チェック
});
revealedクラスで二重実行を防ぐ
一度フェードインした画像に .revealed クラスを付与し、:not(.revealed) で除外することでスクロールするたびに同じ画像が再アニメーションされるのを防いでいます。スクロール連動アニメーションの全般はスクロールに合わせたフェードインアニメーションも参照してください。

4. ループ再生(最後の画像の後に繰り返す)

最後の画像までフェードインしたら最初に戻ってループするパターンです。ヒーロー画像やバナーの自動再生に使えます。

$(function () {
  var $imgs      = $('.loop-img');
  var DURATION   = 800;   // フェードインの時間
  var HOLD_TIME  = 2000;  // 表示保持時間
  var FADE_OUT   = 400;   // フェードアウトの時間

  function playLoop(index) {
    $imgs.stop(true).animate({ opacity: 0 }, 0);  // 全てリセット

    $($imgs[index])
      .animate({ opacity: 1 }, DURATION)
      .delay(HOLD_TIME)
      .animate({ opacity: 0 }, FADE_OUT, function () {
        var next = (index + 1) % $imgs.length;  // 最後の次は0に戻る
        playLoop(next);
      });
  }

  playLoop(0);
});
stop(true)を忘れるとアニメーションが蓄積する
ループ処理では毎回 stop(true) で前のアニメーションキューをクリアしてから次を開始します。これを省略するとアニメーションが蓄積して、後半になるほど動作がおかしくなります。

5. クリックで次の画像を表示するスライドショー風

「次へ」ボタンを押すたびに次の画像がフェードインするパターンです。前後ボタンと現在位置インジケーターも実装します。

<div id="slideshow">
  <img class="slide" src="photo1.jpg" alt="1">
  <img class="slide" src="photo2.jpg" alt="2">
  <img class="slide" src="photo3.jpg" alt="3">
</div>
<div id="slide-controls">
  <button id="prev-btn">◀ 前へ</button>
  <span id="slide-counter">1 / 3</span>
  <button id="next-btn">次へ ▶</button>
</div>
#slideshow { position: relative; }
.slide {
  display: none;
  width: 100%;
}
.slide.is-active {
  display: block;
}
$(function () {
  var $slides  = $('.slide');
  var current  = 0;
  var total    = $slides.length;

  // 初期表示
  $slides.eq(current).addClass('is-active').css('opacity', 1);

  function goTo(index) {
    var $current = $slides.eq(current);
    var $next    = $slides.eq(index);

    $current.fadeOut(300, function () {
      $current.removeClass('is-active');
      $next.addClass('is-active').css('opacity', 0).fadeIn(400);
      current = index;
      $('#slide-counter').text((current + 1) + ' / ' + total);
    });
  }

  $('#next-btn').on('click', function () {
    goTo((current + 1) % total);
  });

  $('#prev-btn').on('click', function () {
    goTo((current - 1 + total) % total);
  });
});

6. グリッドギャラリーへの応用(千鳥遅延)

複数列のグリッドに画像が並んでいる場合、列ごと・行ごとに遅延を変えると洗練された演出になります。

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.gallery-item {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.gallery-item.visible {
  opacity: 1;
  transform: translateY(0);
}
// CSSトランジション+jQueryでクラスを付与するアプローチ
$(function () {
  var COL_COUNT = 3;    // グリッドの列数
  var STAGGER   = 100;  // 1セルごとの遅延(ms)

  $('.gallery-item').each(function (i) {
    var $el  = $(this);
    var col  = i % COL_COUNT;  // 列番号(0,1,2,0,1,2...)
    var row  = Math.floor(i / COL_COUNT);  // 行番号
    var delay = (row + col) * STAGGER;     // 行+列で遅延を決定

    setTimeout(function () {
      $el.addClass('visible');
    }, delay);
  });
});
CSSトランジション+クラス付与で滑らかに
opacity だけでなく transform: translateY(20px) から translateY(0) へのCSSトランジションを合わせることで、上から浮かび上がるような演出になります。jQueryの animate() は JavaScript でスタイルを制御しますが、CSSトランジションはGPUで処理されるためモバイルでも滑らかです。

7. animate()とCSSトランジションの使い分け

方式 コード量 パフォーマンス コールバック 向いている用途
jQuery animate() 少ない 普通(JSで制御) あり(完了後に処理できる) 順番制御・ループ・コールバック必要な場合
jQuery fadeIn/fadeOut() 最少 普通 あり シンプルな表示/非表示
CSSトランジション+クラス付与 中程度 高い(GPU処理) transitionend イベントで対応 ギャラリー・大量画像・モバイル重視
結論:シーケンシャル制御はanimate()、大量表示はCSSトランジション
「1枚ずつ順番に、前が終わってから次へ」という厳密な制御が必要な場合は animate() のコールバックが便利です。ギャラリーの初回読み込みアニメーションのように「ざっくりずらして表示するだけ」ならCSSトランジション+setTimeout() でクラスを付与する方がパフォーマンスに優れます。

まとめ

画像を1枚ずつフェードインする実装は、用途によって使い分けることが重要です。基本の再帰関数を理解すれば、ループ・スクロール連動・スライドショーなど様々な応用に展開できます。

関連記事: アニメーション完全ガイド(animate・fadeIn/Out・slideToggle) / スクロールに合わせたフェードインアニメーション / マウスオーバーで画像を切り替える方法

よくある質問(FAQ)

Qページを開いたら最初から全部表示されてしまいます。
ACSSで .seq-img { opacity: 0; } を設定していないことが原因です。jQueryが読み込まれる前に一瞬すべて表示されるのを防ぐため、スタイルシートで初期状態を非表示(opacity:0 または display:none)にしておく必要があります。
Q全部の画像が同時にフェードインしてしまいます。
Aeach() で全てに animate() を呼ぶと全てが同時に開始されます。1枚ずつ順番にするには、前のアニメーションの完了コールバック内で次を開始する再帰関数(セクション1)か、delay(i * 間隔)(セクション2)を使ってください。
Qフェードインの速度を変えたいです。
Aanimate({ opacity: 1 }, 600) の第2引数がミリ秒単位の時間です。600(0.6秒)を 1000(1秒)などに変更してください。"slow"(600ms)・"fast"(200ms)の文字列も使えます。
Q2枚ずつ同時にフェードインしたい場合は?
Aセクション2の delay() を使い、delay(Math.floor(i / 2) * INTERVAL) のようにインデックスを2で割ることで2枚ずつ同じ遅延時間を設定できます。
Q画像の読み込みを待ってからフェードインしたいです。
A<img>load イベント(または $(window).on("load"))を使って、全画像の読み込みが完了してからフェードインを開始します。$(function(){})(DOMContentLoaded)ではなく $(window).on("load", function(){}) に変えるだけで対応できます。