SCSSによる雪のエフェクトを解説してみた

## 初めに

はじめましての人ははじめまして
ディップ株式会社のUX/フロントエンドエンジニアの@tato_lolです。

今回アドベントカレンダーの参加を決めあぐねていたところ、
先輩から発破をかけられて結局参加したのですが、
そのころには予約可能日が少ししか残っておらず、クリスマスイブに投稿することになりました。

せっかくなのでクリスマスらしく、
「雪が降るエフェクトをどのようにSCSSで実装するのか？」について解説しようと思います。

## 解説するコード

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="html,result" data-user="alphardex" data-slug-hash="dyPorwJ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Snow (Pure CSS)">
  <span>See the Pen <a href="https://codepen.io/alphardex/pen/dyPorwJ">
  Snow (Pure CSS)</a> by alphardex (<a href="https://codepen.io/alphardex">@alphardex</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>


今回はこちらの alphardex さんのコードを解説します。

### HTML構造

このdivがたくさん（コード内だと200個）並んでいるだけです。簡単ですね。

```html
<div class="snow"></div>
```

### SCSS

#### 雪のデザイン
雪のデザインは以下のように 10px * 10px の白い丸として作成しています。
position は絶対位置で指定するのがポイントです。

```scss
.snow {
  position: absolute;
  width: 10px;
  height: 10px;
  background: white;
  border-radius: 50%;
}
```

#### 雪の見た目のランダム化
雪は透明度や位置・大きさがランダムで表示されますが、
これは SCSS の `random()` を使用して作成されています。

```scss
// 0.0001vw ~ 100vwでX位置指定
$random-x: random(1000000) * 0.0001vw;

// 拡大率を0.0001 ~ 1で指定
$random-scale: random(10000) * 0.0001;

//$iは1 ~ 200までの数字
&:nth-child(#{$i}) {
//透明度を0.0001 ~ 1で指定
  opacity: random(10000) * 0.0001;

// アニメーションのための位置と拡大率の初期設定
  transform: translate($random-x, -10px) scale($random-scale);
}
```

`transform`でX方向の初期位置と雪の拡大率がそれぞれランダムで設定されています。（`-10px`は、Y方向の画面外に表示することで突然雪が現れないようにする工夫です）

#### アニメーション
雪が降ってくるアニメーションは以下のように作られています

```scss
@function random_range($min, $max) {
  // scssのランダム関数：引数を取らない場合は0 ~ 1未満
  $rand: random();

  // $min ~ $maxの値を出力するよう指定 
  $random_range: $min + floor($rand * (($max - $min) + 1));
  @return $random_range;
}

// 10秒 ~ 30秒でアニメーションを実行
$fall-duration: random_range(10, 30) * 1s;

// -1秒 ~ -30秒でアニメーションの起動タイミングを設定（delayをマイナス値にすると実行途中のアニメーションが表示される）
$fall-delay: random(30) * -1s;

// 0.0001vw ~ 100vwでX位置指定
$random-x: random(1000000) * 0.0001vw;

// XY方向の位置指定に揺らぎを発生させるためのoffset
$random-offset: random_range(-100000, 100000) * 0.0001vw;

// X方向の向き変更前の終端位置（-10vw ~ 110vw）
$random-x-end: $random-x + $random-offset;

// X方向の向き変更後の終端位置（-5vw ~ 105vw）
$random-x-end-yoyo: $random-x + ($random-offset / 2);

// 向き変更のタイミング（0.3 ~ 0.8）
$random-yoyo-time: random_range(30000, 80000) / 100000;

// Y方向の向き変更前の終端位置（30vh ~ 80vh）
$random-yoyo-y: $random-yoyo-time * 100vh;

// 拡大率を0.0001 ~ 1で指定
$random-scale: random(10000) * 0.0001;

//$iは1 ~ 200までの数字
&:nth-child(#{$i}) {
  animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
}
@keyframes fall-#{$i} {
// $random-yoyo-timeを%に変更（30% ~ 80%）
  #{percentage($random-yoyo-time)} {
    transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
  }
      
  to {
    transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
  }
}
```

自然なX方向の揺らぎをもった雪を上から降らせ、途中で向きを反転させ一番下まで進むというアニメーションを実装しています。

反転後はoffsetを2で割ることでX方向の動きが半分に抑えられることで自然に上から落ちてくる感を表現しています。


## まとめ

CSS、とりわけSCSSに慣れていない方にはわかりづらそうなエフェクトですが、
中々面白い作りをしていてこれからSCSSを学ぶ人には参考になる部分が多いかと思います！

「クリスマスっぽいもの作ってよ」といった無茶ぶりにもこれで対応できますね（？？？）


ここまで読んでくださりありがとうございました。