CSSアニメーションとは、JavaScriptを使わずにCSSだけで要素の動きや変化を実現する技術である。@keyframesでアニメーション定義、transitionで状態間の変化、transformで移動・回転・拡縮を制御する。will-changeやtransform: translateZ(0)でGPUアクセラレーションを有効にすると60fpsの滑らかな動きが実現できる。prefers-reduced-motionメディアクエリで動きを抑制するアクセシビリティ配慮も必須。
CSSアニメーションの基本
CSSで要素に動きをつける方法は大きく分けて2つある。transition と animation(@keyframes)だ。まずはこの2つの違いを明確にしておこう。
transition と animation(@keyframes) の違い
| 特徴 |
transition |
animation(@keyframes) |
| トリガー |
状態変化(:hover, :focus, class追加)が必要 |
ページ読み込み時に自動再生できる |
| 中間状態 |
開始と終了の2点間のみ |
複数のキーフレームで自由に制御 |
| 繰り返し |
不可(元に戻るだけ) |
infinite で無限ループ可能 |
| 向いている場面 |
ホバー、フォーカス、状態切替 |
ローディング、入場演出、永続アニメ |
transition は「AからBへの変化」をなめらかにする。一方 animation は「A→B→C→D…」と複数の段階を自由に定義でき、自動再生やループも可能だ。ホバー時の色変化なら transition で十分だが、入場アニメーションやローディング表現には animation が必要になる。
animation プロパティの構文
animation プロパティはショートハンドで8つの値を一行で指定できる。
animation: name duration timing-function delay iteration-count direction fill-mode play-state;
/* 例 */
animation: fadeInUp 0.6s ease-out 0s 1 normal forwards running;
/* 最小構成(名前と秒数だけでも動く) */
animation: fadeInUp 0.6s;
| プロパティ |
意味 |
初期値 |
| animation-name |
@keyframes で定義したアニメーション名 |
none |
| animation-duration |
アニメーションの所要時間 |
0s |
| animation-timing-function |
加速・減速の曲線(イージング) |
ease |
| animation-delay |
開始までの遅延時間 |
0s |
| animation-iteration-count |
繰り返し回数(infinite で無限) |
1 |
| animation-direction |
再生方向(normal / reverse / alternate) |
normal |
| animation-fill-mode |
アニメ前後の状態(forwards / backwards / both) |
none |
| animation-play-state |
再生/一時停止(running / paused) |
running |
@keyframes の書き方
@keyframes でアニメーションの各段階を定義する。from / to の2段階指定と、パーセンテージで複数段階を指定する方法がある。
/* from / to(2段階) */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* パーセンテージ(複数段階) */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
Point
animation-fill-mode: forwards を指定しないと、アニメーション終了後に要素が初期状態に戻ってしまう。フェードインで表示した要素が一瞬で消えるのはこれが原因だ。入場アニメーションには forwards を必ず指定しよう。
コピペで使えるCSSアニメーション20選
基本を押さえたら、実際のパターン集が本番だ。以下の20パターンは実務で頻繁に使われるアニメーションを6カテゴリに分類し、コードをそのまま流用できるように用意した。CSSアニメーションジェネレーターで値を自由にカスタマイズすることもできる。
フェード系(4パターン)
1. フェードイン
最も基本的な入場アニメーション。要素をふわっと表示する。ページ読み込み時やモーダル表示に。
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fadeIn {
animation: fadeIn 0.6s ease-out forwards;
}
2. フェードアウト
要素を自然に消す退場アニメーション。通知やトーストメッセージの非表示に。
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.fadeOut {
animation: fadeOut 0.4s ease-in forwards;
}
3. フェードインUp(下から浮き上がる)
スクロール連動の入場演出で最も人気が高いパターン。下から20pxほどスライドしながらフェードインする。
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fadeInUp {
animation: fadeInUp 0.6s ease-out forwards;
}
4. フェードインDown(上から降りてくる)
ヘッダーの固定表示やドロップダウンメニューの出現に。上から降りてくる動きは「現れる」印象を与える。
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fadeInDown {
animation: fadeInDown 0.6s ease-out forwards;
}
スライド系(4パターン)
5. スライドインLeft(左から)
サイドバーやドロワーメニューの出現に。画面外からスライドして入ってくる演出だ。
@keyframes slideInLeft {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.slideInLeft {
animation: slideInLeft 0.5s ease-out forwards;
}
6. スライドインRight(右から)
通知パネルやカルーセルのスライド遷移に。左からの逆バージョン。
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.slideInRight {
animation: slideInRight 0.5s ease-out forwards;
}
7. スライドインUp(下から)
モバイルのボトムシートやCTAバーの出現に。画面下から持ち上がる動き。
@keyframes slideInUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.slideInUp {
animation: slideInUp 0.5s ease-out forwards;
}
8. スライドインDown(上から)
アラートバーやクッキー同意バナーの表示に。ページ上部から降りてくる。
@keyframes slideInDown {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.slideInDown {
animation: slideInDown 0.5s ease-out forwards;
}
スケール・バウンス系(4パターン)
9. ズームイン
モーダルやライトボックスの表示に。中央から拡大しながら出現する。
@keyframes zoomIn {
from {
opacity: 0;
transform: scale(0.5);
}
to {
opacity: 1;
transform: scale(1);
}
}
.zoomIn {
animation: zoomIn 0.4s ease-out forwards;
}
10. バウンス(弾む)
注目させたい要素やCTAボタンに。弾むような動きで視線を引きつける。
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-30px);
}
60% {
transform: translateY(-15px);
}
}
.bounce {
animation: bounce 1s ease infinite;
}
11. パルス(脈打つ)
通知バッジやオンラインステータスの表示に。要素が呼吸するように拡大・縮小を繰り返す。
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
.pulse {
animation: pulse 2s ease-in-out infinite;
}
12. ラバーバンド(伸縮)
ユーザーの操作に対するフィードバックに。ゴムのように伸び縮みする遊び心のあるエフェクト。
@keyframes rubberBand {
0% { transform: scaleX(1) scaleY(1); }
30% { transform: scaleX(1.25) scaleY(0.75); }
40% { transform: scaleX(0.75) scaleY(1.25); }
50% { transform: scaleX(1.15) scaleY(0.85); }
65% { transform: scaleX(0.95) scaleY(1.05); }
75% { transform: scaleX(1.05) scaleY(0.95); }
100% { transform: scaleX(1) scaleY(1); }
}
.rubberBand {
animation: rubberBand 0.8s ease;
}
回転・フリップ系(3パターン)
13. スピン(永続回転)
ローディングアイコンやリフレッシュインジケーターに。シンプルだが使用頻度は非常に高い。
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.spin {
animation: spin 1s linear infinite;
}
14. フリップX(横回転)
カードめくりやコンテンツ切替に。Y軸を中心にくるっと回転する。
@keyframes flipX {
from {
transform: perspective(400px) rotateY(90deg);
opacity: 0;
}
to {
transform: perspective(400px) rotateY(0deg);
opacity: 1;
}
}
.flipX {
animation: flipX 0.6s ease-out forwards;
}
15. フリップY(縦回転)
カード裏表の切替やプライスカードの演出に。X軸を中心に縦方向に回転する。
@keyframes flipY {
from {
transform: perspective(400px) rotateX(90deg);
opacity: 0;
}
to {
transform: perspective(400px) rotateX(0deg);
opacity: 1;
}
}
.flipY {
animation: flipY 0.6s ease-out forwards;
}
ローディング系(3パターン)
16. ドットローダー(3つの点が跳ねる)
データ読み込み中の表示に。3つの点が順番に跳ねるパターン。animation-delay でタイミングをずらすのがポイントだ。
@keyframes dotBounce {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
/* HTML: <div class="dotLoader"><span></span><span></span><span></span></div> */
.dotLoader span {
display: inline-block;
width: 12px;
height: 12px;
margin: 0 4px;
background: #6366f1;
border-radius: 50%;
animation: dotBounce 1.4s ease-in-out infinite both;
}
.dotLoader span:nth-child(1) { animation-delay: -0.32s; }
.dotLoader span:nth-child(2) { animation-delay: -0.16s; }
.dotLoader span:nth-child(3) { animation-delay: 0s; }
17. スピナー(回転ローダー)
フォーム送信中やページ遷移時の待機表示に。ボーダーの一辺だけ色を変えて回転させる定番パターン。
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(99, 102, 241, 0.2);
border-top-color: #6366f1;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
18. プログレスバー
ファイルアップロードやステップ進行の表示に。バーが左から右に伸びる直感的な表現。
@keyframes progressBar {
from {
width: 0%;
}
to {
width: 100%;
}
}
.progressBar {
height: 4px;
background: #6366f1;
border-radius: 2px;
animation: progressBar 2s ease-in-out forwards;
}
ホバーエフェクト系(2パターン)
19. シェイク(横揺れ)
エラー表示やフォームのバリデーションエラーに。「ダメ」を直感的に伝える横揺れ。
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.shake {
animation: shake 0.6s ease;
}
20. ハートビート(心臓の鼓動)
お気に入りボタンや「いいね」の演出に。心臓の鼓動のように2段階で拡大する。
@keyframes heartbeat {
0% { transform: scale(1); }
14% { transform: scale(1.3); }
28% { transform: scale(1); }
42% { transform: scale(1.3); }
70% { transform: scale(1); }
}
.heartbeat {
animation: heartbeat 1.5s ease-in-out infinite;
}
ツールで効率化
上記20パターンすべてをCSSアニメーションジェネレーターで値を微調整してカスタマイズできる。duration、delay、イージングをスライダーで変えるだけで、プロジェクトに合ったアニメーションが完成する。
イージング関数(timing-function)の使い分け
イージング関数はアニメーションの「加速・減速」のカーブを制御するプロパティだ。同じ動きでもイージングを変えるだけでまったく違う印象になる。選び方には明確な指針がある。
| イージング |
特徴 |
使い所 |
ease |
ゆっくり始まり、加速し、ゆっくり終わる |
汎用(デフォルト) |
ease-in |
ゆっくり始まり、加速して終わる |
退場アニメーション |
ease-out |
速く始まり、ゆっくり終わる |
入場アニメーション |
ease-in-out |
ゆっくり始まり、ゆっくり終わる |
ループアニメーション |
linear |
等速(加速も減速もなし) |
ローディング、回転 |
cubic-bezier() |
カスタムカーブ |
こだわりの動きを表現 |
cubic-bezier() の実用例
cubic-bezier() は4つの制御点で独自のイージングカーブを定義できる。以下はWebでよく使われる実用的なカスタムイージングだ。
/* バウンス風(オーバーシュートして戻る) */
animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* スプリング風(弾むような弾力感) */
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
/* シャープ(キレのある動き) */
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
/* マテリアルデザインの標準イージング */
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
Point
入場アニメーションには ease-out、退場アニメーションには ease-in を使うのが基本原則だ。人間の直感に合う動きになる。「入ってくるものは勢いよく来てゆっくり止まる」「出ていくものはゆっくり動き出して加速する」と覚えておけばいい。
CSSアニメーションはブラウザのレンダリングパイプラインに直結する。使い方を誤るとモバイルで露骨にカクつく。パフォーマンスを維持しながらアニメーションを使うための3つの原則を押さえておきたい。
transform と opacity のみをアニメーションさせる
ブラウザのレンダリングには「レイアウト → ペイント → コンポジット」の3段階がある。transform と opacity の変更はコンポジット段階のみで処理されるため、GPUアクセラレーションが効いて高速だ。一方 width、height、margin、top/left のアニメーションはレイアウトの再計算を引き起こすため、極力避けるべきだ。
/* 良い例: transform と opacity だけを変更 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 悪い例: top をアニメーションさせている(レイアウト再計算) */
@keyframes fadeInUpBad {
from {
opacity: 0;
top: 20px;
}
to {
opacity: 1;
top: 0;
}
}
will-change プロパティの使い方
will-change はブラウザに「この要素はこれからアニメーションするよ」と事前に伝えるプロパティだ。GPUレイヤーを事前に確保することで、アニメーション開始時の一瞬のカクつきを防げる。
/* アニメーション対象の要素に指定 */
.animate-target {
will-change: transform, opacity;
}
/* アニメーション完了後に解除するのが理想 */
.animate-target.is-done {
will-change: auto;
}
注意
will-change を全要素に指定するのは逆効果だ。GPUメモリを消費し、むしろパフォーマンスが悪化する。実際にアニメーションする要素だけに限定し、不要になったら will-change: auto に戻すこと。
prefers-reduced-motion への対応(アクセシビリティ)
前庭障害を持つユーザーや、動きに酔いやすいユーザーのために、OS設定で「視差効果を減らす」を有効にしている場合がある。prefers-reduced-motion メディアクエリでこれを検知し、アニメーションを無効化またはシンプルな動きに変更するのがアクセシビリティのベストプラクティスだ。
/* アニメーションを完全に無効化する */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* または、動きを控えめなフェードだけにする */
@media (prefers-reduced-motion: reduce) {
.fadeInUp {
animation: fadeIn 0.3s ease-out forwards;
/* translateY の動きを省略し、opacity のみに */
}
}
Point
prefers-reduced-motion 対応は「やって当然」の時代になっている。WCAG 2.1 のガイドライン 2.3.3(動きによるアニメーション)でも、不要な動きを抑制する手段の提供が推奨されている。CSS数行で対応できるので、プロジェクトの初期段階でグローバルCSSに入れておくのがベストだ。
JavaScriptとの連携
CSSアニメーションだけでは「スクロールしたら発火」「ボタンクリックで再生」といった動的な制御ができない。JavaScriptと組み合わせることで、ユーザーの操作やスクロール位置に応じたアニメーション制御が実現する。
Intersection Observer でスクロール時にアニメーション発火
Intersection Observer は、要素がビューポートに入ったタイミングを検知するAPIだ。スクロールイベントをリスナーで監視するよりもパフォーマンスに優れている。
/* CSS: 初期状態は非表示 */
.js-animate {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.js-animate.is-visible {
opacity: 1;
transform: translateY(0);
}
// JavaScript: ビューポートに入ったらクラスを付与
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target); // 一度だけ発火
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.js-animate').forEach((el) => {
observer.observe(el);
});
classList.add() でクラス切替によるアニメーション制御
ボタンクリック時にアニメーションを再生する場合は、アニメーション用のクラスを動的に追加する。一度付与したクラスを外して再付与すればリプレイも可能だ。
// ボタンクリックでシェイクアニメーションを再生
const button = document.querySelector('.js-shake-trigger');
const target = document.querySelector('.js-shake-target');
button.addEventListener('click', () => {
target.classList.remove('shake');
// ブラウザにリフロー(再計算)を強制して、クラス再付与を認識させる
void target.offsetWidth;
target.classList.add('shake');
});
animationend イベントの活用
animationend イベントを使うと、CSSアニメーションの終了タイミングをJavaScriptで検知できる。アニメーション完了後にDOMを削除したり、次のアニメーションをチェーンしたりする場合に使う。
const element = document.querySelector('.notification');
// アニメーション終了後にDOMから削除
element.addEventListener('animationend', (e) => {
if (e.animationName === 'fadeOut') {
element.remove();
}
});
// フェードアウトを開始
element.classList.add('fadeOut');
ツールで効率化
アニメーションの duration や easing を何度も調整するのは手間がかかる。CSSアニメーションジェネレーターでリアルタイムにプレビューしながら値を詰め、完成したコードをコピーすれば効率的だ。