CSS 画面切り替え アニメーション9選
CSS 画面切り替え アニメーションとは、ページを読み込み直さずに、画像が別の画像に変わったり、注文が確定したり、フォームが次の段階へ進んだりするとき、その変化を滑らかに見せる短い動きのことです。
自動再生 · タイルを押すとその項目へ · すべて1つのzip
- 01 ブラインド開閉の登場
- 02 ポラロイドの揺れ登場
- 03 グリッド⇔リスト表示切替
- 04 メイン画像スライド切替
- 05 フィルターチップのスロット埋め
- 06 カートへ飛ぶ追加
- 07 チケット破り確認
- 08 ステップ方向スライド
- 09 斜めシーン交代
9種は人気順ではなく、訪問者がページで出会う順番に並べています。最初の場面がブラインドのように開き(01)、写真がひもに吊るされて揺れながら現れ(02)、見方の切り替えがグリッドとリストの間を滑り(03)、目を引く大きな画像が同じ場所で入れ替わり(04)、選んだ条件がチップとしてスロットへ飛び込み(05)、追加した商品が放物線を描いてカートへ届き(06)、確認の瞬間にチケットが破れ(07)、フォームが進む方向に合わせて流れ(08)、最後に場面全体が斜めに裂けて次へ変わります(09)。
| 番号 | 名前 | 何が動くか | JS |
|---|---|---|---|
| 01 | ブラインド開閉の登場 | スラット6枚の scaleY 連鎖 |
なし |
| 02 | ポラロイドの揺れ登場 | カードの rotate 減衰振動 |
なし |
| 03 | グリッド⇔リスト表示切替 | カードの translate 座標 |
なし |
| 04 | メイン画像スライド切替 | シーンの translateX 交差 |
なし |
| 05 | フィルターチップのスロット埋め | チップの translate 飛行 |
なし |
| 06 | カートへ飛ぶ追加 | 点の二重 translate 放物線 |
なし |
| 07 | チケット破り確認 | 左右の rotate 分離 |
なし |
| 08 | ステップ方向スライド | パネルの translateX ±100% |
なし |
| 09 | 斜めシーン交代 | skew の板が通過 |
なし |
01ブラインド開閉の登場
ページが開くと、明るい見出しパネルを覆っていた横ブラインド6枚が上から順に折りたたまれ、下の文を現します。ヒーロー見出しが一度に現れるのではなく、スラット一枚ずつ落ち着いて開いていくのが違いです。
.reveal__slat {
transform-origin: top center;
transform: scaleY(1);
animation: slat-open $duration $easing both;
}
@keyframes slat-open {
from { transform: scaleY(1); }
to { transform: scaleY(0); }
}
// スラットごとに90msずつ遅らせて階段のように
.reveal__slat:nth-child(2) { animation-delay: 90ms; }
.reveal__slat:nth-child(3) { animation-delay: 180ms; }
02ポラロイドの揺れ登場
上端一点に吊るされたポラロイド写真カードが、傾いたまま落ちてきて左右に数回揺れた後、まっすぐ収まります。ギャラリーの導入や旅の記録サムネイルに置くと、静止的な写真ページに手作りの温かさが生まれます。
.swing {
transform-origin: top center; // ピン一点が回転軸
animation: swing-in $duration $easing both;
}
@keyframes swing-in {
0% { transform: translateY(-70px) rotate(18deg); opacity: 0; }
38% { transform: rotate(-11deg); }
56% { transform: rotate(7deg); }
72% { transform: rotate(-4deg); }
100% { transform: rotate(0deg); }
}
03グリッド⇔リスト表示切替
同じ商品カード3枚が、2列のグリッドと1列のリストの間を、画面の外に出ることなく行き来します。トグルのつまみとカードが同時に動くので、触れた瞬間に画面が生きている感覚になります。
.vs__card { transition: transform $duration $easing; }
// % はカード自身の大きさ基準 — グリッド: 右へカード1枚分
.vs__card--2 { transform: translate(calc(100% + 8px), 0); }
.vs__card--3 { transform: translate(0, calc(100% + 10px)); }
// リスト: 同じカードが1行ずつ下へ
#vs-list:checked ~ .vs__board .vs__card--2 { transform: translate(0, calc(100% + 10px)); }
#vs-list:checked ~ .vs__board .vs__card--3 { transform: translate(0, calc(200% + 20px)); }
04メイン画像スライド切替
下のサムネイルを選ぶと、今の画像が左へ抜け、新しい画像が右から同じ枠に入ります。商品ページで画像が瞬きなく流れるように入れ替わるため、選ぶ楽しさが続きます。
.gal__scene {
position: absolute; inset: 0;
transform: translateX(110%); // 待機: 右の外
transition: transform $duration $easing, opacity $duration $easing;
}
#gal-2:checked ~ .gal__frame .gal__scene--2 { transform: translateX(0); }
#gal-2:checked ~ .gal__frame .gal__scene--1 { transform: translateX(-110%); } // 左へ退場
05フィルターチップのスロット埋め
色チップを選ぶと、そのチップが列から上の空きスロットへそのまま飛んで、同じ大きさでぴたりと収まります。選んだという事実が文字の変化ではなく物の移動として見えるので、買い物の画面では特に伝わりやすいです。
.pick__chip { transition: transform $duration $easing; }
// チップごとにスロットまでの距離が違う — チップ2は左へ96px・上へ76px
#pick-2:checked ~ .pick__row .pick__chip--2 {
transform: translate(-96px, -76px);
z-index: 2; // 飛ぶチップが上に浮く
}
#pick-2:checked ~ .pick__slot { border-color: rgba(255,255,255,.8); }
06カートへ飛ぶ追加
「追加」を押すと、商品色の点がカードから右上のカート印まで放物線で飛び、透明になって消え、個数のバッジが弾んで知らせます。出発点と到着点が同じ画面にあるので、自分の操作がどこへ届いたかを目で追えます。
.cart__fly { transition: transform $duration $easing; } // 外側: x は等速
.cart__fly-y { transition: transform $duration $easing; } // 内側: y はイージング
// 着地座標は getBoundingClientRect の実測値
#cart-add:checked ~ .cart__fly { transform: translateX(273px); opacity: 0; }
#cart-add:checked ~ .cart__fly .cart__fly-y { transform: translateY(-71px); }
#cart-add:checked ~ .cart__bag .cart__badge { transform: scale(1.35); }
07チケット破り確認
確認を押した瞬間、チケットの中央の点線が光り、左右の半分が逆方向へ傾いて開き、破れた2枚として画面から抜けます。注文確定やチェックインのような後戻りできない瞬間を、文字の変化より確実に刻みます。
.tear__half { transition: transform $duration $easing, opacity $duration $easing; }
#tear-ok:checked ~ .tear__ticket .tear__half--l {
transform: translate(-52px, 26px) rotate(-9deg); // 左半分: 左下へ
opacity: 0;
}
#tear-ok:checked ~ .tear__ticket .tear__half--r {
transform: translate(44px, 30px) rotate(8deg); // 右半分: 右下へ
opacity: 0;
}
08ステップ方向スライド
次の段階へ進むと現在のパネルが左へ抜け、新しいパネルが右から入り、戻るときは方向がちょうど逆になります。矢印を読まなくても流れの方向が目でわかるので、何段階あるフォームでも迷わなくなります。
.wiz__panel { transition: transform $duration $easing, opacity $duration $easing; }
.wiz__panel--2 { transform: translateX(100%); opacity: 0; } // ステップ2の居場所: 右
// 戻るとステップ1は左の居場所へ、ステップ2は右へ戻る — 方向が逆になる
#wiz-2:checked ~ .wiz__window .wiz__panel--1 { transform: translateX(-100%); opacity: 0; }
#wiz-2:checked ~ .wiz__window .wiz__panel--2 { transform: translateX(0); opacity: 1; }
#wiz-1:checked ~ .wiz__window .wiz__panel--1 { transform: translateX(0); opacity: 1; }
09斜めシーン交代
画面を斜めに分ける2枚の板が同じ方向へタイミングをずらしながら滑って通り過ぎ、後ろに隠れていた次の場面を一度に現します。ランディングページの季節や企画の場面を、読み込み直しではなくこの動作ひとつで劇的に切り替えられます。
.scene__wipe {
top: -20%; bottom: -20%; left: -45%;
width: 190%;
transform: skewX(-14deg) translateX(-115%); // 斜めに寝かせた板
transition: transform $duration $easing;
}
#scene-swap:checked ~ .scene__frame .scene__wipe { transform: skewX(-14deg) translateX(115%); }
.scene__inner { transition: opacity $dur-quick linear calc(#{$duration} / 2); } // 板が隠す中央で交代
どこで壊れるか — 落とし穴ひとつ
カートへ飛ぶ点の最初の版は、確かに動いているのに着地がどこかずれていました。カードからカートまでの距離を目分量で計算して translateX(247px) を入れていたところ、Playwright で実際の座標を測るとバッジの中心は予想より右にあり、点はバッジに26px届かない空中で消えていました。getBoundingClientRect で出発点と到着点の中心を測り直して translateX(273px) と translateY(-71px) に直したところ、点はバッジのど真ん中に着地するようになりました。同じ検査で、カゴの持ち手を描く疑似要素が親の位置基準を掴めずステージの隅に浮いていたのも見つかり、親に position: relative を1行足して貼り直しました。飛ぶ動きは目分量ではなく座標で測って狙うべきで、この9ファイルの圧縮パスワードは 2aq7sbjtであり、どれもこの実測を経てから公開しています。
アクセシビリティ
9種すべて prefers-reduced-motion: reduce では動きだけを切り、結果の状態を残します。01はブラインドが開いた状態で見出しがすぐ見え、02は揺れずにまっすぐ吊るされ、03・04・08はカード・シーン・パネルが遷移なしで即座に切り替わり、05はチップがすぐスロットに収まり、06は点が瞬間移動してバッジだけが膨らみ、07は破れる演出なしで即座に消え、09は板が通過せず場面だけが変わります。クリック対象はすべて実際のラジオ・チェックボックスとラベルで結ばれているためキーボードとスクリーンリーダーでも操作でき、飾りの要素は aria-hidden で読み上げ順から外しています。transform による遷移がなめらかな理由は MDN の transform リファレンスに、動きを減らす設定の意味は MDN の prefers-reduced-motionにまとまっています。ページ間の遷移は CSS ページ遷移アニメーション9種に、タブパネルの遷移は タブメニュー遷移まとめに別途まとめています。
FAQ
本当にJavaScriptを1行も書かなくていいのですか?
この9種はすべてCSSだけで動きます。クリックが必要なものは、見えないラジオ・チェックボックスを置いて :checked の状態にCSSが従う形にしてあるので、貼り付けた瞬間からそのまま動きます。React版は状態管理のために useState を使います。
自分のサイトに付けるとき、何を変えればよいですか?
動く部分のクラス名と距離の値を、自分の画面の大きさに合わせるだけです。特に05のチップ飛行と06のカート飛行は、チップ・カード・カート印の実際の位置によって translate の数値が変わるので、開発者ツールで要素の座標を測ってから入力するのが確実です。
View Transitions API でも同じ効果は出せますか?
View Transitions API は文書や大きな領域全体が変わる瞬間のためのブラウザ機能で、この記事のような小さな状態変化には合わず、対応状況もブラウザごとにまだ差があります。この9種は対応の差が出ないよう transform と opacity だけを使い、どこでも同じ見た目で動くようにしています。