【CSSアニメーション】Webクリスマスカードを作ってみた

N高ネットコース2年の [@YukiMihashi](https://twitter.com/YukiMihashi) です。
この記事は [N高等学校 (1) Advent Calendar 2020](https://qiita.com/advent-calendar/2020/n-highschool) の15日目の記事です。

CSSアニメーションで遊びながら制作した [Webクリスマスカード 2020](https://yuki384.github.io/christmas-2020/) の解説をしていきます。

本当は100%CSSと書きたいところですが、手紙の内容が時間によって変わるという仕組みや、モーダルウィンドウ表示の仕組みの簡略化のため、一部JavaScriptを使用しています…

## 概要
手紙やツリーのイラストがあり、クリックするといろいろ起こる、というWebのクリスマスカードです。アニメーションなので実際に見てみてください。（PC推奨ですが、スマホサイズでも動きます。IE対応はしておりません）
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264538/140a0b72-d7b0-a0c4-c735-2abc95a5b8f6.png)](https://yuki384.github.io/christmas-2020/)

GitHub レポジトリ: https://github.com/yuki384/christmas-2020/
作品ページ: https://yuki384.github.io/christmas-2020/

## アニメーションの実装
3種類のアニメーションがありますが、すべてCSSで実装しています。

### 飾りがゆれるアニメーション
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264538/59cf6d34-4128-b858-4330-bedf6e9f9b37.png)

まずはじめに、ベルや丸い形の飾りなどが左右に揺れるアニメーションを解説します。

[`animation`](https://developer.mozilla.org/ja/docs/Web/CSS/animation)を使えば、ずっと見た目を動かすことができます。アニメーションが何割まで進んだタイミングで動かすか、どのように動かすか、などが指定できるので、表現の幅はかなり幅広いですね。

CSSは以下のようになっています。

```css
#kazari {
  transform-origin: 140px 700px; /* 回転の中心を設定 */
  animation: rotate 2s ease-in-out -1s infinite alternate; /* アニメーションを再生 */
}
@keyframes rotate {
  0% {transform: rotate(6deg);} /* 角度を変える */
  100% {transform: rotate(-6deg);} /* 角度を変える */
}
```

#### 回転 
角度を変えるには[`transform: rotate();`](https://developer.mozilla.org/ja/docs/Web/CSS/transform-function/rotate())を使いました。アニメーションの動きなどを設定するには、[`@keyframes`](https://developer.mozilla.org/ja/docs/Web/CSS/@keyframes)を使います。ここでは単に初めと終わりを設定しているだけですが、細かくタイミングを設定することもできます。
<details><summary>CSSのコード</summary><div>

```css
@keyframes rotate {
  0% {transform: rotate(6deg);}
  100% {transform: rotate(-6deg);}
}
```
</div></details>


#### アニメーション
CSSの[`animation`](https://developer.mozilla.org/ja/docs/Web/CSS/animation)を使用し、先程書いたゆらゆらと左右に回転するアニメーションを再生します。すべて時間をバラバラに調整することで、揺れが不自然になりません。ここでは`infinite`でずっと揺らしていますが、一回のみの再生とすることもできます。
<details><summary>CSSのコード</summary><div>

```css
#kazari {
  animation: rotate 2s ease-in-out -1s infinite alternate;
}
```
</div></details>


#### 回転の中心を合わせる
このままだと[`transform: rotate();`](https://developer.mozilla.org/ja/docs/Web/CSS/transform-function/rotate())の中心がずれてしまっているので、[`transform-origin`](https://developer.mozilla.org/ja/docs/Web/CSS/transform-origin)を設定します。これは回転させながらxとyの値を微調整していくしかありませんね。
<details><summary>CSSのコード</summary><div>

```css
#kazari {
  transform-origin: 140px 700px;
  animation: rotate 2s ease-in-out -1s infinite alternate;
}
```
</div></details>


これで、飾りが左右に揺れるようになりました。

### プレゼントが震えるアニメーション ₍₍⁽⁽🎁₎₎⁾⁾
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264538/e82bbf25-9d28-378d-a11c-210632488a3c.png)

次に、ツリーの下にあるプレゼントが震えるアニメーションを解説します。
CSSアニメーションは何秒かおきに再生する、ということがJavaScriptのようにはできないので、工夫しました。
0%から85%の間は動かさず（同じ値を設定する）、85%~100%の間に動くようにしています。あとは、[`animation-delay`](https://developer.mozilla.org/ja/docs/Web/CSS/animation-delay)や[`animation-duration`](https://developer.mozilla.org/ja/docs/Web/CSS/animation-duration)を調整して、程よい動きにしましょう。

```css
#present {
  animation: yure 5s ease-in-out infinite alternate;
}

@keyframes yure {
  0% {transform: translate(2px);}
  85% {transform: translate(2px);}
  90% {transform: translate(-2px);}
  95% {transform: translate(2px);}
  100% {transform: translate(-2px);}
}
```



### 手紙が開くアニメーション
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264538/04d0ecb8-812f-cce5-fabf-a286017c3fe4.png)

最後に、手紙が開くアニメーションの解説をします。

HTMLは以下のようになっています。`checkbox`があるのは、checkboxがチェックされているかどうかで手紙の表示・非表示を切り替えているためです。
手紙はこの`checkbox`の`<label>`となっているので、クリックで表示される、という仕組みです。

SVGの`<g>`要素は、他のSVG要素をグループ化するものです。ここでは、手紙の上部分のパスを`<g>`でまとめ、そのタグに `id`を設定しています。

```html
<input type="checkbox" id="text-check">
<div class="text">（手紙の内容）</div>
<div class="letter">
  <label for="text-check">
    <svg 省略> 省略 <g id="letter-top"> 省略 </g> 省略 </svg>
  </label>
</div>
```

この時点では、こうなります。クリックしても、✅が切り替わるだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264538/636b0a10-6980-d0c7-3a2d-09bec95f7e6f.png)

そこで、見た目をつけていきます。

CSSは以下になります。

```css
#text-check {
  display: none; /* チェックボックスは非表示に */
}
#letter-top {
  transition: all .6s; /* 状態変化のスピードを設定 */
  transform-origin: 0 6%; /* 回転の中心を設定 */
}
#text-check:checked ~ .letter #letter-top {
  transform: rotateX(180deg) rotateZ(2deg); /* チェック時の角度を設定 */
}
```

細かい解説をします。

#### チェックボックスは非表示にする
チェック状態で要素の状態を切り替える（後述）だけなので、見た目的には不要です。
<details><summary>CSSのコード</summary><div>

```css
#text-check {
  display: none;
}
```
</div></details>

#### 回転する
チェックボックスがチェックされている時は、そのあとにある`.letter`という要素の子要素`#letter-top`を回転する、というCSSを書きます。
「チェックされている時」は[疑似セレクター `:checked`](https://developer.mozilla.org/ja/docs/Web/CSS/:checked)で、兄弟要素は[一般兄弟結合子(`~`)](https://developer.mozilla.org/ja/docs/Web/CSS/General_sibling_combinator) で、指定しています。

3次元の回転である[`rotateX()`](https://developer.mozilla.org/ja/docs/Web/CSS/transform-function/rotateX()), [`rotateZ()`](https://developer.mozilla.org/ja/docs/Web/CSS/transform-function/rotateZ())を使ってリアルな回転の動きを再現します。

また、先程のゆらゆら揺れる飾りと同じように、回転の中心を設定しました。
<details><summary>CSSのコード</summary><div>

```css
#letter-top {
  transform-origin: 0 6%;
}
#text-check:checked ~ .letter #letter-top {
  transform: rotateX(180deg) rotateZ(2deg);
}
```
</div></details>


#### スピードを設定する
今回は動きの実装で[`animation`](https://developer.mozilla.org/ja/docs/Web/CSS/animation)を使わないため、状態が変化するときのスピードを[`transition`](https://developer.mozilla.org/ja/docs/Web/CSS/transition)で設定することで、アニメーションしているような見た目をつくります。
<details><summary>CSSのコード</summary><div>

```css
#letter-top {
  transition: all .6s;
}
```
</div></details>


## おまけ: モーダルウィンドウの実装
今回はアニメーションについて主に解説したので、手紙クリック時の文章のモーダルウィンドウの実装についての説明は軽くにしておきます。
最後に解説した手紙と同じように、[`checkbox`](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/checkbox)を使って、チェック状態であれば表示、でなければ非表示、とします。閉じるための要素とトリガーとなる要素をそれぞれ[`<label>`](https://developer.mozilla.org/ja/docs/Web/HTML/Element/label)にすれば、簡単に実装が可能です。

ちなみに、プレゼントなどの飾りをクリック→イラスト表示 の流れはCSSではなく、JavaScriptで実装しました。

## おまけ: イラスト→SVG
これは技術とはちょっと異なりますが…イラストを書いた方法です。
気になった方が入れば参考にしてください🔽

1. コンビニで買ったSALASAというボールペンで自由に書く
2. それをiPhoneで撮影しコントラスト等いじって白黒画像にする
3. Adobe Illustratorで「画像トレース」する
4. 「ライブペイント」で色を付ける
5. SVG書き出しをする

## まとめ
以上、CSSアニメーションを使った簡単な実装例の紹介でした。このように、CSSでも様々な動きをつけることができます。
今回ご紹介したのはシンプルなものですが、CSSの世界に興味を持っていただけたら幸いです。

GitHub レポジトリ: https://github.com/yuki384/christmas-2020/
作品ページ: https://yuki384.github.io/christmas-2020/