🏋️

ChristmasだからCSSで雪を降らせよう

2024/12/23に公開

はじめに

こんにちは。GENDAエンジニアの西尾です。
この記事は GENDA Advent Calendar 2024 24日目の記事です。

https://qiita.com/advent-calendar/2024/genda

今日はクリスマスイブなので、CSSで雪を降らせました。

TL;DR

  • CSSでアニメーションを表現する方法は主に2つある。
  • 1つはtransitionを使った仕組みで、あるCSSプロパティの値が変化したとき、その変化をスムーズに行うような仕組みでアニメーションを表現できる。
  • もう1つは@keyframesとanimationプロパティを利用した仕組みで、時間に沿って要素を継続的に動かす場合に便利である。

CSSで雪が降るよ

.snowflakeクラスを定義してJSを使わずに簡単に雪を降らせられます。

サンプルコード
<style>
    /* customizable snowflake styling */
    .snowflake {
      color: #fff;
      font-size: 1em;
      font-family: Arial, sans-serif;
      text-shadow: 0 0 5px #000;
    }
     
    .snowflake,.snowflake .inner{animation-iteration-count:infinite;animation-play-state:running}@keyframes snowflakes-fall{0%{transform:translateY(0)}100%{transform:translateY(110vh)}}@keyframes snowflakes-shake{0%,100%{transform:translateX(0)}50%{transform:translateX(80px)}}.snowflake{position:fixed;top:-10%;z-index:9999;-webkit-user-select:none;user-select:none;cursor:default;animation-name:snowflakes-shake;animation-duration:3s;animation-timing-function:ease-in-out}.snowflake .inner{animation-duration:10s;animation-name:snowflakes-fall;animation-timing-function:linear}.snowflake:nth-of-type(0){left:1%;animation-delay:0s}.snowflake:nth-of-type(0) .inner{animation-delay:0s}.snowflake:first-of-type{left:10%;animation-delay:1s}.snowflake:first-of-type .inner,.snowflake:nth-of-type(8) .inner{animation-delay:1s}.snowflake:nth-of-type(2){left:20%;animation-delay:.5s}.snowflake:nth-of-type(2) .inner,.snowflake:nth-of-type(6) .inner{animation-delay:6s}.snowflake:nth-of-type(3){left:30%;animation-delay:2s}.snowflake:nth-of-type(11) .inner,.snowflake:nth-of-type(3) .inner{animation-delay:4s}.snowflake:nth-of-type(4){left:40%;animation-delay:2s}.snowflake:nth-of-type(10) .inner,.snowflake:nth-of-type(4) .inner{animation-delay:2s}.snowflake:nth-of-type(5){left:50%;animation-delay:3s}.snowflake:nth-of-type(5) .inner{animation-delay:8s}.snowflake:nth-of-type(6){left:60%;animation-delay:2s}.snowflake:nth-of-type(7){left:70%;animation-delay:1s}.snowflake:nth-of-type(7) .inner{animation-delay:2.5s}.snowflake:nth-of-type(8){left:80%;animation-delay:0s}.snowflake:nth-of-type(9){left:90%;animation-delay:1.5s}.snowflake:nth-of-type(9) .inner{animation-delay:3s}.snowflake:nth-of-type(10){left:25%;animation-delay:0s}.snowflake:nth-of-type(11){left:65%;animation-delay:2.5s}
    </style>
    <div class="snowflakes" aria-hidden="true">
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
      <div class="snowflake">
        <div class="inner"></div>
      </div>
    </div>

1

どうやってんねん

HTMLとCSSだけでアニメーションを実装する方法は主に以下の2つです。

  1. トランジション(transition) を使う方法
  2. キーフレームアニメーション(@keyframes + animationプロパティ) を使う方法

transition

トランジションは「あるCSSプロパティの値が変化したとき、その変化をスムーズに行う」という仕組みです。イベントやホバー時など、「状態の変化」を伴う場合に便利です。

以下のような構文で書けます。

.selector {
  transition: プロパティ 対象時間 イージング 遅延時間;
}
  • プロパティ: width, height, background-color など、トランジションでアニメーションさせるCSSプロパティ
  • 対象時間: 変化を何秒かけて行うか (例: 0.3s, 1s)
  • イージング: 変化の速度のカーブ (例: linear, ease-in, ease-out, cubic-bezier(...))
  • 遅延時間: 変化を何秒待ってから始めるか (オプション)

以下のサンプルでは、マウスのホバー時に0.3秒かけて色が滑らかに変わるようなアニメーションを表現しています。

<style>
  /* ボタンのスタイル */
  .btn {
    background-color: #3498db;
    color: #fff;
    padding: 10px 20px;
    border: none;
    cursor: pointer;
    /* ボタンの背景色が変化するときに0.3秒かけて変化する */
    transition: background-color 0.3s ease-in-out;
  }
  
  .btn:hover {
    background-color: #2ecc71;
  }
</style>
<button class="btn">Hover me!</button>

2

keyframesアニメーション

一方で、keyframesアニメーションは、「状態の変化」ではなく、「時間に沿って要素を継続的に動かす」ようなアニメーションを作りたいときに便利です。

以下のような形式で、アニメーションの「どの時点(0%〜100%)でどのようにスタイルを変化させるか」を記述します。

@keyframes slide-down {
  0% {
    transform: translateY(-50px);
    opacity: 0;
  }
  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

以下のサンプルでは"Hello CSS Animation"の文字列が2秒かけて現れるアニメーションを表現しています。

<style>
@keyframes fadein {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

.box {
  animation-name: fadein;
  animation-duration: 2s;       /* 2秒かけて変化 */
  animation-timing-function: ease-in-out; 
  animation-iteration-count: 1; /* 1回のみ再生 */
  animation-fill-mode: forwards; /* 終了後、最終状態を保持 */
}
</style>

<div class="box">Hello CSS Animation</div>

3

CSSのアニメーションプロパティは他にも色々あります。

プロパティ 説明
animation-name @keyframes で定義したアニメーション名を指定 animation-name: slide-down;
animation-duration アニメーションの所要時間 animation-duration: 2s;
animation-timing-function アニメーション速度のカーブ (ease, linear, ease-in, ease-out, cubic-bezierなど) animation-timing-function: ease;
animation-iteration-count アニメーションを何回繰り返すか (数値または infinite) animation-iteration-count: 3;
animation-delay アニメーション開始を遅らせる時間 animation-delay: 1s;
animation-fill-mode アニメーション終了後、どの状態を維持するか (none, forwards, backwards, both) animation-fill-mode: forwards;
animation-direction 再生方向 (normal, reverse, alternateなど) animation-direction: alternate;
animation-play-state アニメーションの再生・一時停止を制御 (running / paused) animation-play-state: running;

今回の雪を降らせたアニメーションもこちらの@keyframeとanimationプロパティを利用しています。縦方向の落下のsnowflakes-fallと左右に揺れるsnowflakes-shakeの2つを組み合わせて雪の感じが表現されていました。

@keyframes snowflakes-fall {
  0%   { transform: translateY(0); }
  100% { transform: translateY(110vh); }
}

@keyframes snowflakes-shake {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(80px); }
}

最後に

CSSでアニメーションを表現する方法を知らなかったので知れて良かった。

参考

GENDA

Discussion