こんにちは、うえむーです!
2月ごろに大宮公園でお花見をしながらLT大会を開催することになったので、テーマを考えていたときに、「HTML/CSS だけで桜やキャラクターを作れたら面白いのでは?」と思いこの作品を作りました。
この記事では、桜の描画やアニメーションの実装方法を詳しく解説します!
作品
実際に作ったのがこちらです。
カービィ・ワドルディが花見をしてるアニメーションを作りました。
CodepenのURL
https://codepen.io/uemuragame5683/pen/WbNyNog
桜の描画はどうなっているのか、どのようなアニメーションを実装しているのかを説明していきます。
コードの説明
桜の木の作成
木の形状は mask-image のプロパティを利用し、色は background のプロパティでグラデーションを設定しました。
木の画像素材を使用して tree.png というファイル名で格納し、以下のように background を設定しました。
.sakura_content_tree {
background: linear-gradient(10deg, #5b2234, #B5753F 30%, #B5753F);
mask-image: url("tree.png");
mask-size: 600px;
mask-repeat: no-repeat;
mask-position: center bottom;
height: 750px;
width: 100%;
position: relative;
overflow: hidden;
}
桜の描画
桜の花びらは radial-gradient を活用して表現しました。
radial-gradient(circle at X% Y%, color1 size1, color2 size2, transparent size3) という形式で、円形のグラデーションを作成できます。
radial-gradient を使い、小さな花びらを複数配置して桜の花を表現しています。
.sakura_content_leaf {
content: "";
background-image:
radial-gradient(circle at 19% 49%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 33% 34%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 20% 61%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 28% 61%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 27% 44%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 27% 53%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 22% 38%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
radial-gradient(circle at 27% 32%, var(--sakura) 0.25%, var(--sakura2) 2.5%, transparent 11%),
.....
background-size: 100% 100%;
display: block;
width: 600px;
height: 500px;
background-repeat: no-repeat;
position: absolute;
top: 0px;
left: -1%;
z-index: 10;
transform: translateX(0%) translateY(0%);
}
この手法を使うことで、画像を使わずに CSS のみで桜の花を描くことができます。
桜吹雪のアニメーション
桜吹雪は li 要素を複数並べてランダムにアニメーションさせることで表現しました。
@keyframes を活用し、落ちる速度や回転の違いを演出しています。
<ul class="sakura">
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
<li class="sakura_inner"></li>
</ul>
.sakura {
position: absolute;
padding: 0px;
margin: 0px;
width: 100%;
height: 75%;
display: flex;
justify-content: center;
align-items: center;
bottom: 10px;
left: 0px;
overflow: hidden;
perspective: 50px;
border-radius: 0% 70%;
& .sakura_inner {
padding: 10px;
position: absolute;
list-style: none;
background: var(--sakura);
border-radius: 0% 80% 0% 60% / 0% 70% 0% 90%;
top: 0px;
&:nth-child(2n) {
background: var(--sakura2);
border-radius: 0% 70% 0% 90% / 0% 90% 0% 70%;
}
&:nth-child(1) {
left: 0px;
animation:
10s ease-in-out 0s infinite normal none running fall,
3s ease-in-out 0s infinite alternate none running sakura_rotate2;
}
&:nth-child(2) {
left: 5%;
animation:
15s ease-in-out 0s infinite normal none running fall,
2s ease-in-out 0s infinite alternate none running sakura_rotate3;
}
・・・
&:nth-child(12) {
left: 90%;
animation:
10s ease-in-out 0s infinite normal sakura_fall,
3.5s ease-in-out 0s infinite alternate sakura_rotate1;
}
}
}
...
@keyframes sakura_fall {
0% {
top: 0%;
}
100% {
top: 150%;
}
}
@keyframes sakura_rotate1 {
0% {
transform: translateX(0px) translateZ(-50px) rotate(0deg);
}
100% {
transform: translateX(100px) translateZ(-30px) rotateY(10deg);
}
}
@keyframes sakura_rotate2 {
0% {
transform: translateX(0px) translateZ(-40px) rotate(0deg);
}
100% {
transform: translateX(100px) translateZ(-60px) rotateY(20deg);
}
}
@keyframes sakura_rotate3 {
0% {
transform: translateX(0px) translateZ(-40px) rotate(10deg);
}
100% {
transform: translateX(100px) translateZ(-60px) rotateY(0deg);
}
}
media queryの指定について
従来の media query の範囲指定は以下だったのですが
// 420px 〜 600px の時
@media (min-width: 420px) and (max-width: 600px) {
...
}
// 420px 以下の時(SMPの時)
@media (max-width: 420px) {
...
}
モダンなmedia query の範囲指定はいかになり、
可読性が高くなり、数学の不等号のように書けるので、直感的に理解しやすくなります。
// 420px 〜 600pxの時
@media (420px <= width <= 600px ) {
...
}
// 420px 以下の時(SMPの時)
@media (420px >= width){
...
}
zoomの設定について
木の部分がpx固定で設定しており、SMPの場合はレイアウトが崩れてしまうので、
それを解決するにはzoomというプロパティを利用しました。
#container {
& .sakura_content, & .kirby_content, & .wado_content, & .bento_content, & .sheet_content {
zoom: 0.5;
}
}
以前は transform: scale() を利用しましたが、top や left の位置がずれる問題ありました。しかし、 zoom を使うとその問題が解消され、簡単に調整できます。
その他
キャラクターやお弁当も すべて CSS で作成 しています!
特に、カービィやワドルディの丸みのあるフォルム、アニメ調の目や口、
さらにお弁当の細かい装飾まで、すべてCSSだけで表現しました。
実際のコード
https://codepen.io/uemuragame5683/pen/WbNyNog
まとめ
今回、HTML/CSS を駆使して「カービィと花見」を作成しました。
・桜の木 : mask-image を利用し、グラデーションで色を設定
・桜の描画 : radial-gradient を活用して花びらを表現
・桜吹雪のアニメーション : @keyframes を使って落下&回転の動きを実装
・モダンな Media Query : @media (420px <= width <= 600px ) で可読性を向上
・レスポンシブ対応 : zoom を使い、SMP(スマホ)でもレイアウト崩れを防止
もし「ここをもっと知りたい!」という点があれば、コメントで教えてください!