Skip to Content
Lecture第7回:CSS装飾とアニメーション

第7回:CSS装飾とアニメーション

🎯 学習目標

  • transformプロパティを理解し活用できる
  • transitionとanimationの違いを理解する
  • 疑似要素・疑似クラスを使いこなせる
  • パフォーマンスを考慮したアニメーションが作れる

📚 導入(5分)

ユーザーエクスペリエンスとアニメーション

アニメーションは単なる装飾ではなく、重要なUX要素です:

  • フィードバック:ユーザーの操作に対する応答
  • 注意誘導:重要な要素への注目を促す
  • 状態変化:画面の変化を自然に見せる
  • ブランディング:サイトの個性を表現

パフォーマンスを考慮したアニメーション

/* ✅ GPUで処理される(高速) */ transform: translateX(100px); transform: scale(1.2); opacity: 0.5; /* ❌ CPUで処理される(低速) */ left: 100px; width: 200px; height: 100px;

💡 理論学習(30分)

transformプロパティ(15分)

translate:移動

.element { /* X軸方向に移動 */ transform: translateX(50px); /* Y軸方向に移動 */ transform: translateY(-30px); /* X・Y軸同時に移動 */ transform: translate(50px, -30px); /* Z軸方向に移動(3D) */ transform: translateZ(10px); /* 3軸同時に移動 */ transform: translate3d(50px, -30px, 10px); /* パーセント指定(要素自身のサイズ基準) */ transform: translate(50%, -50%); /* 中央配置によく使用 */ }

rotate:回転

.element { /* Z軸周りに回転(2D回転) */ transform: rotate(45deg); transform: rotate(0.25turn); /* 1turn = 360deg */ transform: rotate(50grad); /* 1grad = 0.9deg */ /* X軸周りに回転 */ transform: rotateX(45deg); /* Y軸周りに回転 */ transform: rotateY(45deg); /* Z軸周りに回転 */ transform: rotateZ(45deg); /* 3D回転 */ transform: rotate3d(1, 1, 0, 45deg); /* x, y, z, 角度 */ }

scale:拡大縮小

.element { /* 均等拡大縮小 */ transform: scale(1.5); /* 1.5倍 */ transform: scale(0.8); /* 0.8倍 */ /* X・Y軸個別指定 */ transform: scaleX(2); /* X軸のみ2倍 */ transform: scaleY(0.5); /* Y軸のみ0.5倍 */ transform: scale(2, 0.5); /* X軸2倍、Y軸0.5倍 */ /* Z軸拡大縮小 */ transform: scaleZ(2); /* 3D拡大縮小 */ transform: scale3d(2, 0.5, 1); }

skew:傾斜

.element { /* X軸方向に傾斜 */ transform: skewX(30deg); /* Y軸方向に傾斜 */ transform: skewY(15deg); /* X・Y軸同時に傾斜 */ transform: skew(30deg, 15deg); }

複数変形の組み合わせ

.element { /* スペース区切りで複数指定 */ transform: translate(50px, 100px) rotate(45deg) scale(1.2); /* 適用順序に注意:右から左に適用される */ transform: scale(1.2) rotate(45deg) translate(50px, 100px); }

transform-origin:変形の基準点

.element { /* デフォルトは中央(50% 50%) */ transform-origin: center center; /* 左上を基準点にする */ transform-origin: top left; transform-origin: 0 0; /* 右下を基準点にする */ transform-origin: bottom right; transform-origin: 100% 100%; /* カスタム基準点 */ transform-origin: 20px 30px; /* 回転の基準点を変更 */ transform: rotate(45deg); transform-origin: top left; /* 左上を軸に回転 */ }

transitionとanimation(15分)

transition:状態変化のアニメーション

.button { background-color: #3498db; transform: scale(1); transition: all 0.3s ease; } .button:hover { background-color: #2980b9; transform: scale(1.05); } /* 個別プロパティで詳細制御 */ .button-detailed { background-color: #3498db; transform: scale(1); /* プロパティ別に設定 */ transition-property: background-color, transform; transition-duration: 0.3s, 0.2s; transition-timing-function: ease, ease-out; transition-delay: 0s, 0.1s; }

timing-function:イージング

.element { transition: transform 0.5s ease; /* ゆっくり始まり、ゆっくり終わる */ transition: transform 0.5s ease-in; /* ゆっくり始まる */ transition: transform 0.5s ease-out; /* ゆっくり終わる */ transition: transform 0.5s ease-in-out; /* ゆっくり始まり、ゆっくり終わる */ transition: transform 0.5s linear; /* 一定速度 */ /* カスタムベジエ曲線 */ transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); /* ステップ関数 */ transition: transform 1s steps(10, end); /* 10段階でアニメーション */ }

animation:キーフレームアニメーション

/* キーフレームの定義 */ @keyframes fadeInUp { 0% { opacity: 0; transform: translateY(30px); } 100% { opacity: 1; transform: translateY(0); } } /* from/to記法 */ @keyframes slideIn { from { transform: translateX(-100%); } to { transform: translateX(0); } } /* 複数ステップ */ @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-30px); } 60% { transform: translateY(-15px); } } /* アニメーションの適用 */ .element { animation: fadeInUp 0.6s ease-out; } /* 詳細制御 */ .element-detailed { animation-name: bounce; animation-duration: 2s; animation-timing-function: ease; animation-delay: 0.5s; animation-iteration-count: infinite; /* 無限ループ */ animation-direction: alternate; /* 往復 */ animation-fill-mode: forwards; /* 最終状態を維持 */ animation-play-state: paused; /* 一時停止 */ }

疑似要素・疑似クラス

/* 疑似クラス */ .button:hover { /* ホバー時 */ } .input:focus { /* フォーカス時 */ } .checkbox:checked { /* チェック時 */ } .element:first-child { /* 最初の子要素 */ } .element:nth-child(2n) { /* 偶数番目 */ } /* 疑似要素 */ .element::before { content: ""; display: block; /* 要素の前に挿入 */ } .element::after { content: ""; display: block; /* 要素の後に挿入 */ } .paragraph::first-letter { font-size: 2em; float: left; /* 最初の文字を装飾 */ }

🛠️ 実習(50分)

インタラクティブカードコンポーネント作成(45分)

HTML構造

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>インタラクティブカード</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="container"> <h1 class="page-title">Interactive Cards</h1> <div class="cards-grid"> <!-- フリップカード --> <div class="card card--flip"> <div class="card__inner"> <div class="card__front"> <div class="card__content"> <h2 class="card__title">フリップカード</h2> <p class="card__text">ホバーで裏面が表示されます</p> <div class="card__icon">🎭</div> </div> </div> <div class="card__back"> <div class="card__content"> <h2 class="card__title">裏面</h2> <p class="card__text">これは裏面の内容です。詳細情報をここに表示できます。</p> <button class="card__button">詳細を見る</button> </div> </div> </div> </div> <!-- グロウカード --> <div class="card card--glow"> <div class="card__content"> <h2 class="card__title">グロウエフェクト</h2> <p class="card__text">ホバーで光るエフェクト</p> <div class="card__icon">✨</div> </div> </div> <!-- スライドカード --> <div class="card card--slide"> <div class="card__image"> <img src="https://picsum.photos/300/200?random=1" alt="カード画像"> </div> <div class="card__content"> <h2 class="card__title">スライドカード</h2> <p class="card__text">コンテンツがスライドして表示</p> </div> <div class="card__overlay"> <div class="card__overlay-content"> <h3>詳細情報</h3> <p>ホバー時に表示される詳細な情報です。</p> <button class="card__button">もっと見る</button> </div> </div> </div> <!-- バウンスカード */ <div class="card card--bounce"> <div class="card__content"> <h2 class="card__title">バウンスアニメーション</h2> <p class="card__text">クリックでバウンスします</p> <div class="card__icon">🏀</div> </div> </div> <!-- ロードカード --> <div class="card card--loading"> <div class="card__content"> <h2 class="card__title">ローディング</h2> <p class="card__text">ローディングアニメーション</p> <div class="loading-spinner"></div> </div> </div> <!-- 3Dカード --> <div class="card card--3d"> <div class="card__content"> <h2 class="card__title">3Dエフェクト</h2> <p class="card__text">立体的な変形エフェクト</p> <div class="card__icon">🎲</div> </div> </div> </div> </div> </body> </html>

CSS(css/style.css)

/* ========================================== CSS変数とリセット ========================================== */ :root { --primary: #667eea; --secondary: #764ba2; --accent: #f093fb; --success: #4facfe; --warning: #f6d365; --danger: #ff6b6b; --text: #333; --text-light: #666; --white: #ffffff; --shadow: 0 4px 15px rgba(0, 0, 0, 0.1); --shadow-hover: 0 8px 30px rgba(0, 0, 0, 0.2); --radius: 12px; --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, var(--primary), var(--secondary)); min-height: 100vh; padding: 2rem 1rem; } /* ========================================== レイアウト ========================================== */ .container { max-width: 1200px; margin: 0 auto; } .page-title { text-align: center; color: var(--white); font-size: 3rem; margin-bottom: 3rem; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } .cards-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; margin-bottom: 2rem; } /* ========================================== 基本カードスタイル ========================================== */ .card { background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; transition: var(--transition); cursor: pointer; height: 300px; position: relative; } .card__content { padding: 2rem; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; } .card__title { font-size: 1.5rem; margin-bottom: 1rem; color: var(--text); } .card__text { color: var(--text-light); line-height: 1.6; margin-bottom: 1rem; } .card__icon { font-size: 3rem; margin-top: 1rem; } .card__button { background: linear-gradient(135deg, var(--primary), var(--secondary)); color: var(--white); border: none; padding: 0.75rem 1.5rem; border-radius: 25px; cursor: pointer; font-weight: bold; transition: var(--transition); } .card__button:hover { transform: translateY(-2px); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); } /* ========================================== フリップカード ========================================== */ .card--flip { perspective: 1000px; } .card__inner { position: relative; width: 100%; height: 100%; transition: transform 0.8s; transform-style: preserve-3d; } .card--flip:hover .card__inner { transform: rotateY(180deg); } .card__front, .card__back { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: var(--radius); } .card__back { background: linear-gradient(135deg, var(--accent), var(--success)); color: var(--white); transform: rotateY(180deg); } /* ========================================== グロウカード ========================================== */ .card--glow { position: relative; } .card--glow::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border-radius: var(--radius); background: linear-gradient(45deg, var(--accent), var(--success), var(--warning)); z-index: -1; opacity: 0; transition: var(--transition); animation: gradient-shift 3s ease infinite; } @keyframes gradient-shift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } } .card--glow:hover::before { opacity: 1; transform: scale(1.05); filter: blur(20px); } /* ========================================== スライドカード ========================================== */ .card--slide { overflow: hidden; } .card__image { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } .card__image img { width: 100%; height: 100%; object-fit: cover; transition: var(--transition); } .card--slide:hover .card__image img { transform: scale(1.1); } .card--slide .card__content { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); z-index: 2; height: auto; padding: 1.5rem; transform: translateY(50%); transition: var(--transition); } .card--slide:hover .card__content { transform: translateY(0); } .card__overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(102, 126, 234, 0.9); color: var(--white); display: flex; align-items: center; justify-content: center; opacity: 0; transition: var(--transition); z-index: 3; } .card--slide:hover .card__overlay { opacity: 1; } .card__overlay-content { text-align: center; transform: translateY(20px); transition: var(--transition); } .card--slide:hover .card__overlay-content { transform: translateY(0); } /* ========================================== バウンスカード ========================================== */ @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0) scale(1); } 10% { transform: translateY(-8px) scale(1.05); } 40% { transform: translateY(-20px) scale(1.1); } 60% { transform: translateY(-10px) scale(1.05); } } .card--bounce:active { animation: bounce 0.8s ease; } /* ========================================== ローディングカード ========================================== */ .loading-spinner { width: 40px; height: 40px; border: 4px solid rgba(102, 126, 234, 0.3); border-top: 4px solid var(--primary); border-radius: 50%; animation: spin 1s linear infinite; margin-top: 1rem; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* ========================================== 3Dカード ========================================== */ .card--3d { transform-style: preserve-3d; transition: transform 0.6s; } .card--3d:hover { transform: rotateX(10deg) rotateY(10deg) scale(1.05); } .card--3d .card__content::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.1)); opacity: 0; transition: var(--transition); border-radius: var(--radius); } .card--3d:hover .card__content::before { opacity: 1; } /* ========================================== レスポンシブ ========================================== */ @media (max-width: 768px) { .page-title { font-size: 2rem; margin-bottom: 2rem; } .cards-grid { grid-template-columns: 1fr; gap: 1.5rem; } .card { height: 250px; } .card__content { padding: 1.5rem; } } /* ========================================== アクセシビリティ ========================================== */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* フォーカス表示 */ .card:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .card__button:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }

📝 まとめ・質疑応答(5分)

アニメーションのパフォーマンス考慮点

✅ 最適化のポイント

  1. GPUアクセラレーション対応プロパティを使用

    • transform
    • opacity
    • filter
  2. 避けるべきプロパティ

    • width, height
    • top, left, right, bottom
    • margin, padding
  3. will-changeの活用

    .element { will-change: transform; }
  4. アクセシビリティの考慮

    @media (prefers-reduced-motion: reduce) { .animated { animation: none; } }

次回予告:JavaScript基礎

次回からプログラミング言語であるJavaScriptを学習します:

  • 変数の宣言方法
  • データ型の理解
  • 制御構造(条件分岐・繰り返し)
  • 関数の基礎

🏠 宿題

  1. オリジナルボタンコレクションの作成

    • 5種類以上のホバーエフェクト
    • アクセシビリティに配慮した実装
  2. ローディングアニメーションの作成

    @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.7; } 100% { transform: scale(1); opacity: 1; } }
  3. CSS変数を使ったテーマシステム

    • ライトテーマ・ダークテーマの切り替え
    • アニメーション付きテーマ変更

📚 参考リソース


次回もお楽しみに!

Last updated on