CSSアニメーションとは

JavaScriptなしで動きのあるUIを実現できます。

2つの方法

  • transition: 状態変化をスムーズに(ホバー時など)
  • animation: 複雑なアニメーション、自動実行

メリット

  • ⚡ パフォーマンスが良い(GPU活用)
  • 📝 コードがシンプル
  • 🎨 デザイナーでも実装可能
  • ♿ アクセシビリティ対応が簡単

1. transition(状態遷移)

プロパティの変化をスムーズにします。

基本的な書き方

.button {
  background: #2a2a2a;
  color: white;
  padding: 1rem 2rem;
  border: none;
  
  /* transition: プロパティ 時間 イージング 遅延; */
  transition: background 0.3s ease 0s;
}

.button:hover {
  background: #ffeb3b;
  color: #2a2a2a;
}

実装結果

複数プロパティ

/* 個別指定 */
.element {
  transition: 
    background 0.3s ease,
    transform 0.2s ease-out,
    box-shadow 0.3s ease;
}

/* 一括指定 */
.element {
  transition: all 0.3s ease;
}

イージング関数

ease          デフォルト(ゆっくり→速く→ゆっくり)
linear        一定速度
ease-in       ゆっくり開始
ease-out      ゆっくり終了
ease-in-out   両端をゆっくり

/* カスタム */
cubic-bezier(0.68, -0.55, 0.265, 1.55)  バウンド効果

イージング比較

ease:
linear:
ease-in-out:

実用例:カードホバー

<div class="card">
  <img src="image.jpg" alt="画像">
  <div class="content">
    <h3>タイトル</h3>
    <p>説明文</p>
  </div>
</div>

<style>
.card {
  border: 3px solid #2a2a2a;
  overflow: hidden;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-10px) scale(1.02);
  box-shadow: 10px 10px 0px rgba(0,0,0,0.2);
}

.card img {
  width: 100%;
  transition: transform 0.5s ease;
}

.card:hover img {
  transform: scale(1.1);
}
</style>

実装結果

カードタイトル

ホバーすると浮き上がります

2. animation(キーフレーム)

複雑なアニメーションを定義できます。

基本的な書き方

/* アニメーション定義 */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* または */
@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

/* 適用 */
.element {
  animation: fadeIn 1s ease;
  /* animation: 名前 時間 イージング; */
}

animationの詳細プロパティ

.element {
  animation-name: fadeIn;              /* アニメーション名 */
  animation-duration: 1s;              /* 時間 */
  animation-timing-function: ease;     /* イージング */
  animation-delay: 0.5s;               /* 遅延 */
  animation-iteration-count: infinite; /* 繰り返し */
  animation-direction: alternate;      /* 方向 */
  animation-fill-mode: forwards;       /* 終了後の状態 */
  animation-play-state: running;       /* 再生状態 */
}

/* 一括指定 */
.element {
  animation: fadeIn 1s ease 0.5s infinite alternate forwards;
}

フェードイン

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.fade-in {
  animation: fadeIn 1s ease;
}

実装結果

フェードイン

スライドイン

@keyframes slideIn {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.slide-in {
  animation: slideIn 0.5s ease;
}

実装結果

スライドイン

バウンス

@keyframes bounce {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-30px);
  }
}

.bounce {
  animation: bounce 0.6s ease infinite;
}

実装結果

回転

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.rotate {
  animation: rotate 2s linear infinite;
}

実装結果

パルス(拡大縮小)

@keyframes pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.2);
    opacity: 0.7;
  }
}

.pulse {
  animation: pulse 1.5s ease infinite;
}

実装結果

3. 実用的なアニメーション集

ローディングスピナー

<div class="spinner"></div>

<style>
@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 50px;
  height: 50px;
  border: 5px solid #f5f5f5;
  border-top-color: #2196f3;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
</style>

実装結果

ドットローディング

<div class="dots">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

<style>
@keyframes dotBounce {
  0%, 80%, 100% { transform: scale(0); }
  40% { transform: scale(1); }
}

.dots {
  display: flex;
  gap: 10px;
}

.dot {
  width: 15px;
  height: 15px;
  background: #2196f3;
  border-radius: 50%;
  animation: dotBounce 1.4s infinite ease-in-out;
}

.dot:nth-child(1) { animation-delay: -0.32s; }
.dot:nth-child(2) { animation-delay: -0.16s; }
</style>

実装結果

通知バッジ

<div class="notification-badge">3</div>

<style>
@keyframes ping {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(2);
    opacity: 0;
  }
}

.notification-badge {
  position: relative;
  background: #f44336;
  color: white;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
}

.notification-badge::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: #f44336;
  animation: ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}
</style>

実装結果

3

シェイク(振動)

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
  20%, 40%, 60%, 80% { transform: translateX(10px); }
}

.shake {
  animation: shake 0.5s;
}

/* エラー時などに使う */
input.error {
  border-color: #f44336;
  animation: shake 0.5s;
}

グラデーション移動

@keyframes gradientMove {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.gradient-bg {
  background: linear-gradient(270deg, #ffeb3b, #4caf50, #2196f3);
  background-size: 200% 200%;
  animation: gradientMove 5s ease infinite;
}

実装結果

グラデーション移動

タイプライター効果

@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}

@keyframes blink {
  50% { border-color: transparent; }
}

.typewriter {
  overflow: hidden;
  border-right: 3px solid #2a2a2a;
  white-space: nowrap;
  animation: 
    typing 3s steps(30, end),
    blink 0.75s step-end infinite;
}

4. パフォーマンス最適化

GPUアクセラレーション

/* ✅ 高速(GPUを使う) */
transform: translateX(100px);
transform: scale(1.5);
transform: rotate(45deg);
opacity: 0.5;

/* ❌ 遅い(CPUを使う) */
left: 100px;
width: 200px;
height: 200px;
margin-left: 100px;

will-changeの使用

/* アニメーション開始前にヒントを与える */
.element:hover {
  will-change: transform, opacity;
}

.element {
  transform: translateX(100px);
  opacity: 0.5;
}

/* 注意: 常に指定すると逆効果 */

複合transformの書き方

/* ✅ 正しい */
transform: translateX(100px) rotate(45deg) scale(1.2);

/* ❌ 間違い(最後のみ適用される) */
transform: translateX(100px);
transform: rotate(45deg);
transform: scale(1.2);

5. アクセシビリティ

アニメーション無効化対応

/* ユーザーがアニメーションを無効にしている場合 */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* または個別に */
@media (prefers-reduced-motion: reduce) {
  .animated-element {
    animation: none;
  }
}

6. 実践例:モーダル

<div class="modal-overlay">
  <div class="modal">
    <h2>モーダル</h2>
    <p>コンテンツ</p>
    <button>閉じる</button>
  </div>
</div>

<style>
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slideUp {
  from {
    transform: translateY(50px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: fadeIn 0.3s ease;
}

.modal {
  background: white;
  padding: 2rem;
  border: 3px solid #2a2a2a;
  max-width: 500px;
  animation: slideUp 0.4s ease;
}
</style>

7. ライブラリ活用

Animate.css

<!-- CDN読み込み -->
<link rel="stylesheet" 
      href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>

<!-- 使用 -->
<div class="animate__animated animate__fadeIn">
  フェードイン
</div>

<div class="animate__animated animate__bounce">
  バウンス
</div>

まとめ

CSSアニメーションで快適なUXを実現しましょう。

使い分け

  • transition: ホバー、フォーカス、状態変化
  • animation: ローディング、通知、複雑な動き

ベストプラクティス

  • transform/opacityを優先(パフォーマンス)
  • アニメーション時間は0.2s〜0.5s(UX)
  • prefers-reduced-motion対応(アクセシビリティ)
  • 必要な場所にのみ適用(パフォーマンス)

学習のステップ

  1. transitionで基礎を学ぶ
  2. @keyframesで複雑な動きを作る
  3. パフォーマンスを理解する
  4. 実際のプロジェクトで活用