【CSS×JS】泣くおじさんで学ぶWebアニメーション入門

## 1. はじめに

- **記事の概要**
    - この記事では、CSSとHTMLを使用して「泣いてるおじさん」のアニメーションを作成する方法を学びます
- **記事で学べること**
    - CSSを使った簡単なアニメーションの作成方法
    - JavaScriptを使ったアニメーションの制御

## 2. 主要な機能と構文

### 2.1 HTMLの作成

- HTMLを使って「泣いてるおじさん」の基本的な構造を作成します
    - 主に`<div>`タグや`<span>`タグを使用して、顔や涙のパーツを表現します
- `<div>`タグを使って顔の輪郭を作り、目や口をそれぞれの`<span>`タグで表現します
    - HTML構造が複雑にならないように、シンプルかつ分かりやすいコードにしました

### 2.2 CSSアニメーションの作成

- CSSの`@keyframes`ルールを使用して、涙が流れるアニメーションを定義します
    - CSSトランジションで目の開閉や口の動きを制御します
- `@keyframes`を使って、涙の位置を上下に動かすアニメーションを作成します
    - `animation`プロパティを使ってアニメーションの再生速度や繰り返し回数を指定します

### 2.3 JavaScriptによるアニメーション制御の作成

- JavaScriptを使って、ユーザーの操作に基づいてアニメーションを開始または停止させる機能を実装します
    - 例えば、ボタンをクリックするとアニメーションが始まるようにします
- `document.getElementById()`や`addEventListener()`を使って、HTML要素にイベントリスナーを追加し、アニメーションの開始や停止を制御します

## 3. 泣くおじさんで「Webアニメーション」を学んでみよう！

![oji.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112294/f8920f03-66a3-bbd4-c906-5b51b8c3f5bf.gif)

### 3.1 「泣くおじさん」のコード全体像

「泣いてるおじさん」を作成するためのHTML、CSS、JavaScriptコードの全体像を示します

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>泣いてるおじさんのアニメーション</title>
  <style>
    /* おじさんの顔のスタイル */
    #face {
      width: 200px;
      height: 200px;
      background-color: #f8d7da;
      border-radius: 50%;
      position: relative;
      margin: 50px auto;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    /* 目のスタイル */
    .eye {
      width: 20px;
      height: 20px;
      background-color: #000;
      border-radius: 50%;
      position: absolute;
      top: 60px;
    }

    .eye.left {
      left: 60px;
    }

    .eye.right {
      right: 60px;
    }

    /* 涙のスタイルとアニメーション */
    .tear {
      width: 10px;
      height: 20px;
      background-color: #00f;
      border-radius: 50%;
      position: absolute;
      top: 80px;
      animation: tear-drop 2s infinite;
    }

    .tear.left {
      left: 65px;
    }

    .tear.right {
      right: 65px;
    }

    @keyframes tear-drop {
      0% { transform: translateY(0); }
      100% { transform: translateY(100px); opacity: 0; }
    }
  </style>
</head>
<body>
  <div id="face">
    <div class="eye left"></div>
    <div class="eye right"></div>
    <div class="tear left"></div>
    <div class="tear right"></div>
  </div>

  <button id="toggle-animation">アニメーションの開始/停止</button>

  <script>
    const tears = document.querySelectorAll('.tear');
    const button = document.getElementById('toggle-animation');
    let isAnimating = true;

    button.addEventListener('click', () => {
      tears.forEach(tear => {
        if (isAnimating) {
          tear.style.animationPlayState = 'paused';
        } else {
          tear.style.animationPlayState = 'running';
        }
      });
      isAnimating = !isAnimating;
    });
  </script>
</body>
</html>

```

### 3.2 コード解説

#### `#face` の解説

このCSSは、おじさんの顔全体を表現しています。

```css
#face {
  width: 200px;
  height: 200px;
  background-color: #f8d7da;
  border-radius: 50%;
  position: relative;
  margin: 50px auto;
  display: flex;
  justify-content: center;
  align-items: center;
}
```

- `width` と `height` 
    - 200x200ピクセルの正方形を作成します
- `background-color: #f8d7da;
    - 薄いピンク色を設定し、肌色を表現します
- `border-radius: 50%;` 
    - 正方形を円形に変形し、顔の丸みを表現します
- `position: relative;` 
    - 子要素（目や涙）の配置の基準点となります
- `margin: 50px auto;` 
    - 上下に50pxの余白を付け、左右は自動で中央揃えにします
- `display: flex;` と `justify-content: center;` および `align-items: center;` 
    - 子要素を中央に配置します

#### `.eye` クラスの解説

このCSSは目を表現しています

```css
.eye {
  width: 20px;
  height: 20px;
  background-color: #000;
  border-radius: 50%;
  position: absolute;
  top: 60px;
}

.eye.left {
  left: 60px;
}

.eye.right {
  right: 60px;
}
```

- `width` と `height` 
    - 20x20ピクセルの正方形を作成します
- `background-color: #000;` 
    - 黒色を設定し、目を表現します
- `border-radius: 50%;` 
    - 正方形を円形に変形し、丸い目を作ります
- `position: absolute;` 
    - 親要素（#face）を基準に絶対位置で配置します
- `top: 60px;` 
    - 上端から60px下に配置します
- `.eye.left` と `.eye.right` 
    - 左目と右目をそれぞれ左右に60px移動させて配置します

#### `.tear` クラスと `@keyframes` の解説

このCSSは涙を表現し、アニメーションを設定しています。

```css
.tear {
  width: 10px;
  height: 20px;
  background-color: #00f;
  border-radius: 50%;
  position: absolute;
  top: 80px;
  animation: tear-drop 2s infinite;
}

.tear.left {
  left: 65px;
}

.tear.right {
  right: 65px;
}

@keyframes tear-drop {
  0% { transform: translateY(0); }
  100% { transform: translateY(100px); opacity: 0; }
}
```

- `width` と `height` 
    - 10x20ピクセルの長方形を作成します
- `background-color: #00f;` 
    - 青色を設定し、涙を表現します
- `border-radius: 50%;` 
    - 長方形を楕円形に変形し、涙滴の形を作ります
- `position: absolute;` 
    - 親要素（#face）を基準に絶対位置で配置します
- `top: 80px;` 
    - 上端から80px下に配置します
- `.tear.left` と `.tear.right` 
    - 左右の涙をそれぞれ配置します
- `animation: tear-drop 2s infinite;`
    - tear-dropという名前のアニメーションを2秒間隔で無限に繰り返します

#### `@keyframes tear-drop`の解説

- `@keyframes tear-drop` では涙が落ちるアニメーションを定義しています
- キーフレームアニメーションは、アニメーションの開始から終了までの中間状態を指定することができ、涙が顔の下方向に流れ落ちる動きを自然に表現できます

```css
@keyframes tear-drop {
  0% { transform: translateY(0); }
  100% { transform: translateY(100px); opacity: 0; }
}
```

- `@keyframes tear-drop`: 
   - この行で、`tear-drop` という名前の新しいキーフレームアニメーションを定義しています

- `0% { transform: translateY(0); }`:
   - これはアニメーションの開始状態（0%）を定義しています
   - `transform: translateY(0)` は、要素を元の位置（Y軸方向に0px）に配置することを意味します
   - つまり、涙はここから落ち始めます

- `100% { transform: translateY(100px); opacity: 0; }`:
   - これはアニメーションの終了状態（100%）を定義しています
   - `transform: translateY(100px)` は、要素をY軸方向に100px下へ移動させることを意味します
   - `opacity: 0` は、要素を完全に透明にします
   - つまり、涙は100px下に移動し、同時に消えていきます


#### アニメーション開始/停止についての解説

- JavaScriptで、ボタンをクリックするたびにアニメーションの開始と停止が切り替わる機能を実現しています
- ユーザーはこのボタンを使って、涙のアニメーションの開始と停止を制御できます

```html
<button id="toggle-animation">アニメーションの開始/停止</button>
```
これは、アニメーションを制御するためのボタン要素です。id 属性を使用して JavaScript から参照できるようにしています。

```javascript
const tears = document.querySelectorAll('.tear');
```
この行では、ドキュメント内のすべての '.tear' クラスを持つ要素（涙を表す要素）を取得し、`tears` 定数に格納しています。

```javascript
const button = document.getElementById('toggle-animation');
```
ここでは、先ほど定義した id='toggle-animation' のボタン要素を取得し、`button` 定数に格納しています。

```javascript
let isAnimating = true;
```
この変数は、現在アニメーションが実行中かどうかを追跡するためのフラグです。初期値は `true` で、アニメーションが実行中であることを示します。

```javascript
button.addEventListener('click', () => {
  // イベントリスナーの中身
});
```
ボタン要素にクリックイベントリスナーを追加しています。ユーザーがボタンをクリックするたびに、この関数が実行されます。

```javascript
tears.forEach(tear => {
  if (isAnimating) {
    tear.style.animationPlayState = 'paused';
  } else {
    tear.style.animationPlayState = 'running';
  }
});
```
この部分では、すべての涙要素（`tears`）に対して繰り返し処理を行っています：
- `isAnimating` が `true` の場合、アニメーションを一時停止（`'paused'`）します
- `isAnimating` が `false` の場合、アニメーションを再開（`'running'`）します
- `animationPlayState` プロパティを使用することで、CSS アニメーションの再生状態を JavaScript から制御しています

```javascript
isAnimating = !isAnimating;
```
最後に、`isAnimating` の値を反転させています。これにより、次にボタンがクリックされたときに、逆の動作（停止中なら開始、実行中なら停止）が行われるようになります。



## 4. Tips

- **実装における注意点とベストプラクティス**
    - CSSのアニメーションは、パフォーマンスを考慮して`transform`や`opacity`を使うと良いです
    - JavaScriptでアニメーションを制御する際には、`animationPlayState`を使うとスムーズに再生や停止が可能です
- **コードの動作原理と背景にある概念の説明**
    - CSSアニメーションは、`keyframes`を使ってアニメーションのステップを定義し、`animation`プロパティで詳細な設定を行います。JavaScriptでは、`style`プロパティを操作することで、アニメーションの状態を動的に変更できます
- **想定されるエラーや例外処理の方法**
    - CSSが正しく適用されない場合は、クラス名やID名が正しいか確認してください。また、ブラウザのデベロッパーツールを使ってスタイルが適用されているかをチェックすることも重要です
- **パフォーマンスや最適化に関する考察**
    - アニメーションのパフォーマンスを向上させるためには、CSSの`transform`や`opacity`を使用することで、リフロー（再描画）を避けることが推奨されます

## 5. まとめ

HTMLとCSSを使って「泣いてるおじさん」を作成し、JavaScriptでアニメーションを制御する方法を学びましたがいかがだったでしょうか。