作成した画像拡大ローディング
一般的なローディング画面は「待たせる」ものですが、このアプローチではローディングそのものがコンテンツの一部になります。画像のパルス、霧エフェクト、テキストの浮上という一連の演出により、待ち時間を印象的な体験に変換します。
<!-- ローディングオーバーレイ -->
<div class="loading-overlay">
<div class="loading-image"></div>
<div class="loading-dots">
<span></span>
<span></span>
<span></span>
</div>
</div>
<!-- 霧のオーバーレイ -->
<div class="fog-overlay"></div>
<!-- ヒーローセクション -->
<section class="hero-section">
<div class="hero-content">
<h1 class="hero-title">Nature Awaits</h1>
<p class="hero-subtitle">Discover the Beauty Around You</p>
</div>
</section> * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Georgia', serif;
overflow: hidden;
background: #0a0a0a;
}
/* ローディングオーバーレイ */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #0a0a0a;
z-index: 9999;
overflow: hidden;
opacity: 1;
animation: fadeOutLoading 0.5s ease 8s forwards;
}
/* 画像本体 - パルス→拡大 */
.loading-image {
width: 100vw;
height: 100vh;
background-image: url('/img/autumn-leaves.jpg');
background-size: cover;
background-position: center;
transform: scale(0.1);
border-radius: 20px;
animation: pulseAndZoom 8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
/* ローディングドット */
.loading-dots {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
gap: 12px;
z-index: 10000;
opacity: 1;
animation: fadeOutDots 0.5s 6s forwards;
}
.loading-dots span {
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
animation: dotPulse 1.5s ease-in-out infinite;
}
.loading-dots span:nth-child(2) {
animation-delay: 0.2s;
}
.loading-dots span:nth-child(3) {
animation-delay: 0.4s;
}
/* ドットのパルスアニメーション */
@keyframes dotPulse {
0%, 100% {
opacity: 0.3;
transform: scale(0.8);
}
50% {
opacity: 1;
transform: scale(1.2);
}
}
/* ドットのフェードアウト */
@keyframes fadeOutDots {
to {
opacity: 0;
}
}
/* 霧のオーバーレイ */
.fog-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.85) 100%);
opacity: 0;
z-index: 10000;
pointer-events: none;
animation: fogLifecycle 10s ease 7.5s forwards;
}
/* パルス→拡大アニメーション */
@keyframes pulseAndZoom {
/* パルス1回目 */
0% {
transform: scale(0.1);
opacity: 0.6;
filter: grayscale(1);
}
12.5% {
transform: scale(0.12);
opacity: 0.7;
filter: grayscale(1);
}
25% {
transform: scale(0.1);
opacity: 0.6;
filter: grayscale(1);
}
/* パルス2回目 */
37.5% {
transform: scale(0.12);
opacity: 0.7;
filter: grayscale(1);
}
50% {
transform: scale(0.1);
opacity: 0.6;
filter: grayscale(1);
}
/* パルス3回目 */
62.5% {
transform: scale(0.12);
opacity: 0.7;
filter: grayscale(1);
}
75% {
transform: scale(0.1);
opacity: 0.6;
filter: grayscale(1);
}
/* カラーに移行 */
87.5% {
transform: scale(0.1);
opacity: 0.8;
filter: grayscale(0);
}
/* 目一杯拡大 */
100% {
transform: scale(4);
opacity: 1;
filter: grayscale(0);
}
}
/* ローディングのフェードアウト */
@keyframes fadeOutLoading {
to {
opacity: 0;
visibility: hidden;
}
}
/* 霧のライフサイクル */
@keyframes fogLifecycle {
0% {
opacity: 0;
}
15% {
opacity: 1;
}
50% {
opacity: 1;
}
70% {
opacity: 0.25;
}
100% {
opacity: 0;
}
}
/* ヒーローセクション */
.hero-section {
position: relative;
width: 100%;
height: 100vh;
background-image: url('/img/autumn-leaves.jpg');
background-size: cover;
background-position: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 0;
animation: fadeInHero 1s ease 8s forwards;
}
@keyframes fadeInHero {
to {
opacity: 1;
}
}
.hero-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(10, 10, 10, 0.6) 0%, rgba(10, 10, 10, 0.3) 100%);
}
.hero-content {
position: relative;
z-index: 1;
text-align: center;
color: white;
}
.hero-title {
font-size: clamp(2.5rem, 8vw, 5rem);
font-weight: 200;
letter-spacing: 0.15em;
margin-bottom: 1.5rem;
opacity: 0;
transform: translateY(40px) scale(0.95);
animation: revealTitle 1.2s cubic-bezier(0.16, 1, 0.3, 1) 9.5s forwards;
text-shadow: 0 2px 20px rgba(0, 0, 0, 0.5);
}
.hero-subtitle {
font-size: clamp(1rem, 3vw, 1.8rem);
font-weight: 300;
letter-spacing: 0.08em;
opacity: 0;
transform: translateY(30px);
animation: revealSubtitle 1s cubic-bezier(0.16, 1, 0.3, 1) 10.2s forwards;
text-shadow: 0 2px 15px rgba(0, 0, 0, 0.4);
}
@keyframes revealTitle {
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes revealSubtitle {
to {
opacity: 0.95;
transform: translateY(0);
}
} AIへのプロンプト例
以下のようなプロンプトをAIに送信します:
秋の紅葉の画像を使った、印象的なローディング画面からヒーローセクションへの遷移を作成してください。
### 初期状態
- 画像を小さく丸く表示
- 画像の中央に3つの白いドットを配置
### パルス動作(約6秒間、3回繰り返し)
- 約2秒間ごとに画像が少し拡大・縮小
- パルス中は常に白黒で表示
- ドットは順番に明滅してローディング中を表現
### 拡大フェーズ(6-8秒)
- ドットを消す
- 画像をカラーに戻す
- 画面いっぱいに拡大
### 霧エフェクト(7.5秒以降)
- 白い霧のようなエフェクトが出現
- しばらく画面全体を真っ白に
- 霧がモヤのように薄くなる
- 最後に完全に晴れる
### ヒーローセクション(8秒以降)
- 同じ画像を背景に表示
- 「Nature Awaits」というタイトルを底から浮かび上がらせる
- 「Discover the Beauty Around You」というサブタイトルを時間差で表示
このパーツの特徴
- 白黒パルスからカラーへの遷移 パルス時は白黒で表示し、パルス終了後にカラーへ移行します。準備中から本番へのコントラストが印象的なローディング表現です
- ローディングドット 画像中央に3つのドットがアニメーション。「処理中」であることを明確に伝えます
- 霧エフェクトによる遷移 パルス終了後、白い霧が現れてゆっくりと晴れていくことで、ローディングからヒーローセクションへ自然に移行します
- 純粋なCSS制御 JavaScriptを使わず、すべてCSSの
@keyframesで制御。タイミングが一元管理され、メンテナンスしやすい設計です
画像は以下のものを使用しています:
UnsplashのBernd Schulzが撮影した写真
コードのポイント
キーフレームによる複雑なアニメーション制御:
8秒間のアニメーションを@keyframesで細かく制御しています。この手法の利点は、すべてのタイミングを一箇所で管理できることです。
@keyframes pulseAndZoom {
/* パルス1回目 (0-2秒 = 0-25%) */
0% {
transform: scale(0.1);
opacity: 0.6;
filter: grayscale(1); /* パルス時=白黒 */
}
12.5% {
transform: scale(0.12);
opacity: 0.7;
filter: grayscale(1); /* パルス時=白黒 */
}
/* ... */
} パーセンテージによる正確な制御
アニメーション全体を8秒とした場合、パーセンテージで以下のように計算できます。
- 12.5% = 8秒 × 0.125 = 1秒 パルスの拡大ピーク
- 25% = 8秒 × 0.25 = 2秒 1回目のパルス終了
- 75% = 8秒 × 0.75 = 6秒 3回目のパルス終了
この計算により、各段階のタイミングを正確にコントロールできます。
複数プロパティの同時変化
transform、opacity、filterを同時に変化させることで、複雑で印象的な演出を実現しています。
transform: scale()サイズの拡大・縮小でパルスの動きを表現opacity透明度の変化で「フォーカスが合う」感覚を演出filter: grayscale()パルス時は白黒、パルス終了後にカラーへ移行し、準備完了を表現
これらを組み合わせることで、単なる拡大縮小ではなく、「生命感のある」パルスアニメーションになります。
レイヤー構造とz-index管理
複数のオーバーレイを重ねて段階的な演出を実現しています。Web開発では、要素の重なり順を正しく管理することが重要です。
.loading-overlay {
z-index: 9999;
}
.loading-dots {
z-index: 10000;
}
.fog-overlay {
z-index: 10000;
pointer-events: none;
}
レイヤーの役割分担
それぞれのレイヤーには明確な役割があります。
- ローディングオーバーレイ(z-index: 9999) 背景の黒い画面と画像を表示する基底レイヤー。全体の土台となる
- ローディングドット(z-index: 10000) 画像の上に表示し、ローディング中であることを視覚的に伝える
- 霧オーバーレイ(z-index: 10000) 最前面で白い霧エフェクトを表示。
pointer-events: noneでクリックをブロックしない
pointer-events: noneの重要性
霧のオーバーレイにはpointer-events: noneを指定しています。これにより、霧が表示されている間でも、その下のヒーローセクションにマウスイベントが届きます。霧が完全に消える前にユーザーがクリックしても、正しく動作するための配慮です。
複数アニメーションの協調動作
各要素のアニメーションタイミングを調整し、全体で一つの演出を構成しています。CSSアニメーションの強みは、このように複数の要素を連携させて動かせることです。
.loading-image {
animation: pulseAndZoom 8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.loading-dots {
animation: fadeOutDots 0.5s 6s forwards;
}
.fog-overlay {
animation: fogLifecycle 10s ease 7.5s forwards;
}
.hero-section {
animation: fadeInHero 1s ease 8s forwards;
}
タイムライン全体の設計
アニメーション全体のタイムラインを可視化すると、以下のようになります。
- 0-8秒
- パルスアニメーション実行。画像が3回パルスし、最後に拡大
- 6-6.5秒
- ドットがフェードアウト。カラー移行の直前に消えることで、移行をスムーズに
- 7.5-17.5秒
- 霧のライフサイクル。出現→真っ白維持→モヤ→消滅という段階的変化
- 8秒~
- ヒーローセクションが表示開始。霧の演出と並行して進行
- 9.5秒~
- タイトルテキストが浮かび上がる。霧が薄くなった頃に登場
- 10.2秒~
- サブタイトルが表示。タイトルの後を追うように時間差で
animation-delayによる時間差制御
animationプロパティの3番目のパラメータ(例: 6s、7.5s)はanimation-delayを指定しています。これにより、各アニメーションの開始タイミングを正確にコントロールできます。
例えば、ドットは6秒待機してからフェードアウトを開始し、霧は7.5秒待機してから出現を開始します。この時間差によって、パルス→拡大→霧という自然な流れが生まれるのです。
forwardsキーワードの役割
すべてのアニメーションにforwardsを指定しています。これは「アニメーション終了後、最終状態を維持する」という意味です。これがないと、アニメーション終了後に元の状態に戻ってしまいます。ローディング画面が再表示されるような不自然な動きを防ぐため、forwardsは必須です。
まとめ
- @keyframesでの精密制御 パーセンテージを使って複数のプロパティを同時にアニメーション。タイミングを一元管理し、保守性を向上
- filter: grayscale()の活用 拡大時カラー、縮小時白黒の切り替えで視覚的なリズムを創出。単調な動きに変化を加える
- レイヤー構造の設計 z-indexで複数のオーバーレイを管理し、段階的な演出を実現。pointer-events制御でユーザビリティを確保
- アニメーションの協調 animation-delayとforwardsを組み合わせて、各要素が連携した一つの流れを作る
- cubic-bezier()イージング Material Designの標準イージング関数(0.4, 0, 0.2, 1)で滑らかで自然な動きを実現
このパーツは、ポートフォリオサイトやランディングページの印象的なオープニングとして活用できます。画像を変えたり、パルスの回数やタイミングを調整したりして、プロジェクトに合わせてカスタマイズしてみましょう。