ページを開いたときに画像が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 だけでなく
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トランジション+
「1枚ずつ順番に、前が終わってから次へ」という厳密な制御が必要な場合は animate() のコールバックが便利です。ギャラリーの初回読み込みアニメーションのように「ざっくりずらして表示するだけ」ならCSSトランジション+
setTimeout() でクラスを付与する方がパフォーマンスに優れます。まとめ
画像を1枚ずつフェードインする実装は、用途によって使い分けることが重要です。基本の再帰関数を理解すれば、ループ・スクロール連動・スライドショーなど様々な応用に展開できます。
関連記事: アニメーション完全ガイド(animate・fadeIn/Out・slideToggle) / スクロールに合わせたフェードインアニメーション / マウスオーバーで画像を切り替える方法
よくある質問(FAQ)
Qページを開いたら最初から全部表示されてしまいます。
ACSSで
.seq-img { opacity: 0; } を設定していないことが原因です。jQueryが読み込まれる前に一瞬すべて表示されるのを防ぐため、スタイルシートで初期状態を非表示(opacity:0 または display:none)にしておく必要があります。Q全部の画像が同時にフェードインしてしまいます。
A
each() で全てに animate() を呼ぶと全てが同時に開始されます。1枚ずつ順番にするには、前のアニメーションの完了コールバック内で次を開始する再帰関数(セクション1)か、delay(i * 間隔)(セクション2)を使ってください。Qフェードインの速度を変えたいです。
A
animate({ 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(){}) に変えるだけで対応できます。