CSSのみで雪が降るアニメーションサンプル

CSS,CSS アニメーション サンプル集

ピュアなCSSのみで雪を降らせてみたアニメーションサンプルです。

以前作成して紹介している雨が降るの雪バージョンになります。

雪が降るCSSアニメーションサンプル

CSSのanimationプロパティを使って出来るだけランダムな感じで雪が降るアニメーションを作成してみた動作サンプルとサンプルコードになります。

See the Pen CSS | Alternately change colors in Table by yochans (@yochans) on CodePen.

HTMLには雪の他、必要に応じて背景画像やテキストを入れれるように親要素 .container を配置しています。
今回は画像、テキストともに適当ですがサンプルとして入れてあります。

<div class="container">
  <img src="https://1-notes.com/images/ishidatami.jpg" border="0" />
	<div class="snows">
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
		<span></span><span></span><span></span><span></span><span></span>
	</div>
	<p>雪を降らせてみた。</p>
</div>

CSSはやはり長めになってしまいましたが、以下のようになります。
長いだけで特に難しい事はしていませんのでCSS好きな人、これから触ってみようって人は是非遊んでみて下さい。

上手な人ならば、もうちょっと雪っぽくできるハズです。

CSSコードの解説は、全コードの下にて個別に紹介しています。

.container {
	position: relative;
	width: 100%;
	background: #06061a;
	overflow: hidden;
	/* for text	 */
	display: flex;
	justify-content: center;
	align-items: center;
}

.container img {
	width: 100%;
	filter: brightness(0.2);
}

.snows {
	position: absolute;
	width: 100%;
	height:100%;
}

.snows span {
	position: absolute;
	top: -10%;
	left: 0;
	width: 1vw;
	height: 1vw;
	background: #FFF;
	border-radius: 50%;
	opacity: 0.8;
	box-shadow: 0 0 1vw 0.5vw rgba(255, 255, 255, 0.4);
	animation: snow-anim 10s 0s linear infinite;
}

/* Side position */
.snows span:nth-child(1) {
	left: 0%;
}

.snows span:nth-child(2) {
	left: 2%;
}

.snows span:nth-child(3) {
	left: 4%;
}

.snows span:nth-child(4) {
	left: 6%;
}

.snows span:nth-child(5) {
	left: 8%;
}

.snows span:nth-child(6) {
	left: 10%;
}

.snows span:nth-child(7) {
	left: 12%;
}

.snows span:nth-child(8) {
	left: 14%;
}

.snows span:nth-child(9) {
	left: 16%;
}

.snows span:nth-child(10) {
	left: 18%;
}

.snows span:nth-child(11) {
	left: 20%;
}

.snows span:nth-child(12) {
	left: 22%;
}

.snows span:nth-child(13) {
	left: 24%;
}

.snows span:nth-child(14) {
	left: 26%;
}

.snows span:nth-child(15) {
	left: 28%;
}

.snows span:nth-child(16) {
	left: 30%;
}

.snows span:nth-child(17) {
	left: 32%;
}

.snows span:nth-child(18) {
	left: 34%;
}

.snows span:nth-child(19) {
	left: 36%;
}

.snows span:nth-child(20) {
	left: 38%;
}

.snows span:nth-child(21) {
	left: 40%;
}

.snows span:nth-child(22) {
	left: 42%;
}

.snows span:nth-child(23) {
	left: 44%;
}

.snows span:nth-child(24) {
	left: 46%;
}

.snows span:nth-child(25) {
	left: 48%;
}

.snows span:nth-child(26) {
	left: 50%;
}

.snows span:nth-child(27) {
	left: 52%;
}

.snows span:nth-child(28) {
	left: 54%;
}

.snows span:nth-child(29) {
	left: 56%;
}

.snows span:nth-child(30) {
	left: 58%;
}

.snows span:nth-child(31) {
	left: 60%;
}

.snows span:nth-child(32) {
	left: 62%;
}

.snows span:nth-child(33) {
	left: 64%;
}

.snows span:nth-child(34) {
	left: 66%;
}

.snows span:nth-child(35) {
	left: 68%;
}

.snows span:nth-child(36) {
	left: 70%;
}

.snows span:nth-child(37) {
	left: 72%;
}

.snows span:nth-child(38) {
	left: 74%;
}

.snows span:nth-child(39) {
	left: 76%;
}

.snows span:nth-child(40) {
	left: 78%;
}

.snows span:nth-child(41) {
	left: 80%;
}

.snows span:nth-child(42) {
	left: 82%;
}

.snows span:nth-child(43) {
	left: 84%;
}

.snows span:nth-child(44) {
	left: 86%;
}

.snows span:nth-child(45) {
	left: 88%;
}

.snows span:nth-child(46) {
	left: 90%;
}

.snows span:nth-child(47) {
	left: 92%;
}

.snows span:nth-child(48) {
	left: 94%;
}

.snows span:nth-child(49) {
	left: 96%;
}

.snows span:nth-child(50) {
	left: 98%;
}

/* snow size */
.snows span:nth-child(4n+1) {
	width: 0.8vw;
	height: 0.8vw;
	box-shadow: 0 0 1vw 0.5vw rgba(255, 255, 255, 0.45);
}

.snows span:nth-child(4n+2) {
	width: 1vw;
	height: 1vw;
	box-shadow: 0 0 1vw 0.7vw rgba(255, 255, 255, 0.4);
}

.snows span:nth-child(4n+3) {
	width: 1.1vw;
	height: 1.1vw;
	box-shadow: 0 0 1vw 1vw rgba(255, 255, 255, 0.35);
}

.snows span:nth-child(4n+4) {
	width: 1.2vw;
	height: 1.2vw;
	box-shadow: 0 0 1vw 1.2vw rgba(255, 255, 255, 0.3);
}

/* animation-duration */
.snows span:nth-child(6n+1) {
	animation-duration: 20s;
}

.snows span:nth-child(6n+2) {
	animation-duration: 15s;
}

.snows span:nth-child(6n+3) {
	animation-duration: 7s;
}

.snows span:nth-child(6n+4) {
	animation-duration: 30s;
}

.snows span:nth-child(6n+5) {
	animation-duration: 20s;
}

.snows span:nth-child(6n+6) {
	animation-duration: 40s;
}


/* animation-delay */
.snows span:nth-child(5n+1) {
	animation-delay: 0s;
}

.snows span:nth-child(5n+2) {
	animation-delay: 12s;
}

.snows span:nth-child(5n+3) {
	animation-delay: 7s;
}

.snows span:nth-child(5n+4) {
	animation-delay: 4s;
}

.snows span:nth-child(5n+5) {
	animation-delay: 10s;
}

/* snow animation */
@keyframes snow-anim {
	0% {
		top: -10%;
		transform: translateX(0);
	}

	35% {
		transform: translateX(3vw);
	}

	65% {
		transform: translateX(-3vw);
	}

	100% {
		top: 100%;
		transform: translateX(0);
	}
}

/* text */
.container p {
	position: absolute;
	padding: 20px;
	margin: 0;
	font-size: 40px;
	font-weight: bold;
	color: #FFF;
}

サンプルコードの作成工程

雪をひとつ作成する

ベースになるひとつの雪を適当な場所に作成します。

最初にHTMLで並べてしまうと、重なり合って調節が困難になってしまうので最初から作る場合はspanタグをひとつにしておきます。

<div class="container">
	<div class="snows">
		<span></span>
	</div>
</div>

幅と高さをvwで指定しています。
vwは画面の幅あるいはブラウザの表示領域幅に合わせた可変サイズです。表示可能な領域幅に応じて拡縮します。

left width height box-shadownth-child() を使ってランダム風に指定していますのでベースの部分では不要でもあります。

.snows span {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 1vw;
	height: 1vw;
	background: #FFF;
	border-radius: 50%;
	opacity: 0.6;
	box-shadow: 0 0 15px 15px rgba(255, 255, 255, 0.4);
	animation: snow-anim 10s 0s linear infinite;
}

雪の初期配置

作成した雪となるspanタグを必要な数だけ複製し、ポジションの left を調整して横一列に並べます。

サンプルでは50個の雪を作成、2%置きに配置されるようにしています。

もっと少なくても多くても良いですが、少なくすると降っている雪も思っているより少なくなります。
雨のアニメーションでは、速さと横移動のランダム性によってごまかせましたが、雪はなかなかなに、ごまかせれない気がしています。作成しながら記事にしていますので、多分です。

50個に指定するのは、大変ですね。
ほんと nth-child(n)n を数値の変数として使えるといいのにっ・・て皆思ってますよね?
SCSS使えとかなしでー

/* Side position */
.snows span:nth-child(1) {
	left: 0%;
}
.snows span:nth-child(2) {
	left: 2%;
}

/* ・・省略・・ */

.snows span:nth-child(49) {
	left: 96%;
}
.snows span:nth-child(50) {
	left: 98%;
}

高さの初期配置はコンテナ要素の表示領域より上、top にて-10% の位置に配置します。
CSSアニメーションで時間差で下に移動させて、移動しきったらまた元に戻して動かします。

雪の一括指定している値を編集します。
また、サイズとbox-shadowプロパティは別途指定しますので削除しておいても問題ありません。

.snows span {
	position: absolute;
	top: -10%;
	background: #FFF;
	border-radius: 50%;
	opacity: 0.6;
	animation: snow-anim 10s 0s linear infinite;
}

イメージとしては、こんな感じです。

CSSで雪を降らせるイメージ図
CSSで雪を降らせるイメージ図

CSSアニメーションにて、左右に揺らしながら下に落としていくことで、順に落ちてくる感じにしたいと考えています。

雪の大きさ

雪の大きさを何種類かにわけます。

サンプルでは nth-child() にて4種類のサイズの雪を作成しています。
4種類ですので4n+1として4個置きに指定されるようにします。

.snows span:nth-child(4n+1) {
	width: 0.8vw;
	height: 0.8vw;
	box-shadow: 0 0 1vw 0.5vw rgba(255, 255, 255, 0.45);
}
.snows span:nth-child(4n+2) {
	width: 1vw;
	height: 1vw;
	box-shadow: 0 0 1vw 0.7vw rgba(255, 255, 255, 0.4);
}
.snows span:nth-child(4n+3) {
	width: 1.1vw;
	height: 1.1vw;
	box-shadow: 0 0 1vw 1vw rgba(255, 255, 255, 0.35);
}
.snows span:nth-child(4n+4) {
	width: 1.2vw;
	height: 1.2vw;
	box-shadow: 0 0 1vw 1.2vw rgba(255, 255, 255, 0.3);
}

雪が降るアニメーション

雪、 .snows span にたいして一括でanimationプロパティを指定しています。

アニメーション時間の 10s とアニメーション開始までの遅延となる 0s は別途上書きしています。

linear でアニメーションの動きを一定挙動にして、 infinite で無限ループを指定しています。

animation: snow-anim 10s 0s linear infinite;

@keyframesは以下のように指定しています。
topを使って下に移動させながら、transform: translateX()で左右に揺らしています。

/* snow animation */
@keyframes snow-anim {
	0% {
		top: -10%;
		transform: translateX(0);
	}
	35% {
		transform: translateX(3vw);
	}
	65% {
		transform: translateX(-3vw);
	}
	100% { top: 100%;
		transform: translateX(0);
	}
}

アニメーション時間をずらす

ここまでのままでは、全ての雪が同じタイミング、同じスピードで横一線に落ちてしまうのでアニメーション時間と開始時間をずらしてランダム風にします。

アニメーション時間は6種類作成、nth-child(6n+x) を使って animation-duration で指定していきます。

/* animation-duration */
.snows span:nth-child(6n+1) {
	animation-duration: 20s;
}
.snows span:nth-child(6n+2) {
	animation-duration: 15s;
}
.snows span:nth-child(6n+3) {
	animation-duration: 7s;
}
.snows span:nth-child(6n+4) {
	animation-duration: 30s;
}
.snows span:nth-child(6n+5) {
	animation-duration: 20s;
}
.snows span:nth-child(6n+6) {
	animation-duration: 40s;
}

アニメーション開始時間をずらず

アニメーションの開始時間をずらします。

アニメーション開始時間は5種類作成、nth-child(5n+x) を使って animation-delay で指定していきます。

/* animation-delay */
.snows span:nth-child(5n+1) {
	animation-delay: 0s;
}
.snows span:nth-child(5n+2) {
	animation-delay: 12s;
}
.snows span:nth-child(5n+3) {
	animation-delay: 7s;
}
.snows span:nth-child(5n+4) {
	animation-delay: 4s;
}
.snows span:nth-child(5n+5) {
	animation-delay: 10s;
}

要素のサイズ、アニメーション時間、アニメーションの開始時間を4種、5種、6種と違う数にする事で、大きいけど始まるのが遅かったり、落ちるのは早かったり、色々なバリエーションをランダム的に指定する事が可能です。
2週目も影響を受けるので、同じパターンで落ちてくる事はなくなりランダムっぽく見えるようになります。

雨のように速さでごまかせなかったので、完成させての自己評価はイマイチですが、なんとか記事として公開しても良いレベルなのではないでしょうか?

CSSが好きな人なら簡単にカスタムできると思いますので、改善版を公開したら教えて下さい。
アイディアは逆輸入します。