CSSアニメーション 入門

# はじめに
CSSアニメーションに関して説明する機会があったため、それらに関してまとめました。
「CSSは書けるがCSSアニメーションがよくわからん!」といった人達向けに書いた記事であり、
CSSアニメーションに関するプロパティを理解できるようになることを目的としています。

デモはCodePen上に置いてあります。編集して変化を見てみると理解が深まると思います。
デモのJavaScriptはjQueryを読み込んでいないと動かないため、ローカル環境でデモを実行する場合はご注意ください。

# CSSアニメーションとは
要素をアニメーションさせるCSSの機能。
`CSS Transition`と`CSS Animation`の2つの機能がある。

## CSS Transitionとは
プロパティに変更があった時に、一定時間でプロパティを変化させる機能。
単純な動きのアニメーションを実装する時に利用する。

### CSS Transitionの利用例 ###

**[Web Fundamentals](https://developers.google.com/web/fundamentals/?hl=en "Web Fundamentals")**
![menu.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/3fe479f2-76fb-80da-1d77-1b432802ab43.gif)
メニューのスライドイン・スライドアウトに`CSS Transition`が利用されている。

**[Udacity](https://www.udacity.com/ "Udacity")**
![hovertransition.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/f9625c9c-f569-971c-6693-2a8044471097.gif)
要素をホバー時の色の変化に`CSS Transition`が利用されている。

## CSS Animationとは
キーフレームアニメーションを適用できる機能。
細かい動きのアニメーションを実装する時に利用する。

### キーフレームアニメーションとは
キーフレームアニメーションとはアニメーションの開始(0%)から終了(100%)までの任意の経過地点に
プロパティを指定できるアニメーションのこと。(プロパティを指定した経過地点のことをキーフレームという)

### CSS Animationの利用例 ###

**[Single Element CSS Spinners](http://projects.lukehaas.me/css-loaders/ "Single Element CSS Spinners")**
![loader.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/e8d933b3-16b6-9afb-fc94-b08a1af86012.gif)
ローディングアニメーションに`CSS Animation`が利用されている。

## 何故CSSアニメーションを利用するのか
- JavaScriptが不要
- 単純なアニメーションならJavaScriptを利用するより実装が容易。
- JavaScriptが書けない人でもアニメーションの実装ができる。
- JavaScriptを利用したアニメーションと比べてパフォーマンスの遜色はない。(利用シーンや実装方法によっては優れていることもある。)

など手軽さやパフォーマンスの点において魅力的だから。
現状、CSSアニメーションが利用されているサイトは多い。

※JavaScriptが不要と書いてありますが、あくまでCSSアニメーションはアニメーション機能しか有していないため、クリック等の何らかのアクションの後にアニメーションを開始させたい場合は、JavaScriptも必要になります。

## CSSアニメーションの使いどころ
アニメーションの実装はなんでもCSSアニメーションを利用すれば良いというわけではない。
以下のページを見てみると、CSSアニメーションを利用した複雑なアニメーションが多数あるが、
「CSSアニメーションだけでこんなことができるぜ!（コストは度外視）」という意図を感じられるデモもある。

- **[CodePen](https://codepen.io/search/pens/?depth=everything&limit=all&order=popularity&page=2&q=css+animation&show_forks=false "CodePen")**
- **[A Collection of Pure CSS Animation Snippets & Demos](http://marketblog.envato.com/whats-hot/pure-css-animation-snippets/ "A Collection of Pure CSS Animation Snippets & Demos")**

そのため、利用シーンにあわせて使い分ける。

> UI 要素の状態の切り替えなど、単純な「ワンショット」遷移にはCSS アニメーションを使用します。
>
> バウンド、停止、一時停止、巻き戻し、スローダウンなど、高度な効果を実現したい場合はJavaScript アニメーションを使用します。

**[CSS vs JavaScript animations](https://developers.google.com/web/fundamentals/design-and-ui/animations/css-vs-javascript?hl=ja "CSS vs JavaScript animations")**



## CSSアニメーションの利用方法
`CSS Transition`と`CSS Animation`を利用するためには以下のプロパティを指定する。

`CSS Transition`を利用できるプロパティ

- transition
- transition-property
- transition-duration
- transition-delay
- transition-timing-function

`CSS Animation`を利用できるプロパティ

- animation
- animation-name
- animation-duration
- animation-timing-function
- animation-delay
- animation-iteration-count
- animation-direction
- animation-fill-mode
- animation-play-state

また`CSS Animation`を利用するためには
適用するキーフレームアニメーションを`@keyframes`で定義する必要がある。

詳細はそれぞれのデモの後に記載。

# CSS Transitionを利用したアニメーションを実装してみる
`CSS Transition`を利用するために、`transition`を指定して要素をアニメーションさせてみる。

## 要素をホバーした時にアニメーションさせてみる
以下は、`.box`をホバーした時に`background`と`width`を変更しているデモ。
`background`が`#000` → `#ccc`へ、`width`が`100px` → `300px`へ**瞬時**に変化していることがわかる。

**[デモを見る](https://codepen.io/soarflat/pen/kXQzgZ "デモを見る")**
![hover2.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/0802b3b7-7c07-f262-c2d0-8299300d11ca.gif)

```html:html
<div class="box"></div>
```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
}

.box:hover {
  background: #ccc;
  width: 300px;
}
```
これを瞬時にではなく、一定時間で変化させたい。(アニメーションさせたい)
今回は0.3秒かけて変化させたいため、`.box`に`transition: all 300ms 0s ease;`の記述を追加してみる。

```html:html
<div class="box"></div>
```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
  transition: all 300ms 0s ease;
}

.box:hover {
  background: #ccc;
  width: 300px;
}
```

**[デモを見る](https://codepen.io/soarflat/pen/BzqPNg "デモを見る")**
![hovertransition.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/4bf97c74-3c59-7e94-1612-b585bcd793eb.gif)
一定時間で変化するようになった。(アニメーションになった。)
このように`transition`を指定することでアニメーションを実装できる。

また、`transition`は複数のプロパティの値をまとめて指定するプロパティのため。
`transition: all 300ms 0s ease;`を以下のように記述できる。

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
  transition-property: all;
  transition-duration: 300ms;
  transition-delay: 0s;
  transition-timing-function: ease;
}
```

## 要素にクラスを付けたり消したりしてアニメーションさせてみる
`CSS transition`自体はプロパティを変更できないため、
上記のデモのように`:hover`等の疑似クラスや、以下のデモのようにJavaScriptでプロパティを変更する必要がある。

以下は「要素を動かす」ボタンをクリック時に、`transform`と`background`を指定した
`.active`を`.box`に付けたり消したりしているデモ。

**[デモを見る](https://codepen.io/soarflat/pen/wWxxbA "デモを見る")**
![toggle.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/a4cedb53-1c10-061e-aeda-08c88a32a4e1.gif)

```html:html
<div class="box"></div>
<input class="button" type="button" value="要素を動かす">
```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
  transition: all 300ms 0s ease;
}

.active {
  transform: translateX(300px);
  background: #ff0000;
}
```

```js:JavaScript
var $box = $('.box');
var $button = $('.button');

$button.on('click', function(){
  $box.toggleClass('active');
});
```

# transition

以下のプロパティの値をまとめて指定できるプロパティ。

- transition-property
- transition-duration
- transition-delay
- transition-timing-function

## transition-property
どのプロパティにアニメーションを適用するか指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/AXPXLV "デモを見る")**
![property.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/7a99625b-b2f2-5eea-40c2-ece78e0c4d30.gif)

`all`を指定すると、全てのプロパティにアニメーションを適用する。
下の要素は`background`を指定しているため、それ以外のプロパティを変更しても
アニメーションが適用されない。(デモでは`transform`を変更しているがアニメーションしない)

## transition-duration
アニメーション開始から終了までの所要時間を指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/akakbY "デモを見る")**
![duration.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/4c21c290-6744-b90a-edf9-a2b8ee352fab.gif)

## transition-delay
アニメーションが開始するまでの遅延時間を指定するプロパティ。

**[デモを見る](http://codepen.io/soarflat/pen/EyZyYQ "デモを見る")**
![delay.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/b743eb6d-75c0-a8e5-a108-572685ce76be.gif)

## transition-timing-function
アニメーションのイージングを指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/mEGEmd "デモを見る")**
![easing.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/4002071e-83f0-f377-272f-21e52eb2a183.gif)

プロパティの値は定義済みの値(`ease`、`linear`、`ease-in`等)か`cubic-bezier()`で指定する。


## CSS Transitionを利用したアニメーションのイベントを取得する

`CSS Transition`を利用したアニメーションの開始時や、終了時などのイベントを取得できる。
以下はアニメーション終了時の`transitionend`イベントを取得しアニメーション回数を加算しているデモ。

**[デモを見る](http://codepen.io/soarflat/pen/AXYzEo "デモを見る")**
![end.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/a4b5c7d7-8ebf-9c26-8e7d-86753e1c8333.gif)

```html:html
<div class="box"></div>
アニメーション回数:<span class="count"></div>
```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
  transition: all 300ms 0s ease;
}

.box:hover {
  background: #ccc;
  width: 300px;
}
```

```js:JavaScript
var $box = $('.box');
var $count = $('.count');
var count = 1;

$box.on('transitionend', function(e){
  if (e.originalEvent.propertyName === 'width') {
    $count.text(count++);
  }
});
```

`transitionend`イベントは**アニメーションを適用したプロパティの数だけイベントが発火する**ため注意。
デモでは`background`と`width`をアニメーションさせているため、1回のアニメーションで2回イベントが発火する。
そのため、1回のアニメーションで1回だけ処理を実行したい場合はデモのように
特定のプロパティのアニメーション終了時に処理を実行する。

# CSS Animationを利用したアニメーションを実装してみる
`CSS Animation`を利用するために、`animation`を指定して要素をアニメーションさせてみる。

## 要素に`rotation`という名前のキーフレームアニメーションを適用してアニメーションさせてみる
アニメーションの開始(0%)、中間(50%)、終了(100%)の地点にプロパティを
指定した`rotation`という名前のキーフレームアニメーションを`.box`に適用したい。
<img width="407" alt="rotation.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/69667/ef8838a9-4c56-0278-4fc4-58a61aa8edf5.jpeg">

前述のとおり、適用するキーフレームアニメーションは定義する必要があるため、
`@keyframes`で`rotation`を定義する。

```html:html
<div class="box"></div>
```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
}

@keyframes rotation {
  0% {
    transform: scale(.3);
  }

  50% {
    transform: scale(.6) rotate(-45deg);
    background: red;
  }

  100% {
    transform: scale(1) rotate(180deg);
    background: blue;
  }
}
```
<img width="121" alt="box.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/69667/51e20994-60e5-64a8-de67-7370174dbe60.jpeg">
キーフレームアニメーションを定義しただけなので、
アニメーションはしていない状態。

定義した`rotation`を`.box`に適用するために、
`.box`に`animation: rotation 2s ease 0s 1 alternate none running;`の記述を追加する。

```html:html
<div class="box"></div>
```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
  animation: rotation 2s ease 0s 1 alternate none running;
}

@keyframes rotation {
  0% {
    transform: scale(.3);
  }

  50% {
    transform: scale(.6) rotate(-45deg);
    background: red;
  }

  100% {
    transform: scale(1) rotate(180deg);
    background: blue;
  }
}
```

**[デモを見る](https://codepen.io/soarflat/pen/ZOrBEG "デモを見る")**
![rotation.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/978b3f73-7227-4a46-6fd3-361f1b78c7ad.gif)

アニメーションした。
このようにキーフレームアニメーションを`@keyframes`で定義をし、
`animation`で指定することでアニメーションを実装できる。

また、`animation`は複数のプロパティの値をまとめて指定するプロパティのため。
`animation: rotation 2s ease 0s 1 alternate none running;`を以下のようにも記述できる。

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
  animation-name: rotation;
  animation-duration: 2s;
  animation-timing-function: ease;
  animation-delay: 0s;
  animation-iteration-count: 1;
  animation-direction: alternate;
  animation-fill-mode: none;
  animation-play-state: running;
}
```

# animation

以下のプロパティの値をまとめて指定できるプロパティ。

- animation-name
- animation-duration
- animation-timing-function
- animation-delay
- animation-iteration-count
- animation-direction
- animation-fill-mode
- animation-play-state

## animation-name
要素に適用するキーフレームアニメーションを指定するプロパティ。
キーフレームアニメーションは`@keyframes`で定義する必要がある。

## animation-duration
アニメーション開始から終了までの所要時間を指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/grdxVz "デモを見る")**
![duration.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/37235961-2f57-53c8-b856-65e10bc48cb4.gif)

## animation-timing-function
アニメーションのイージングを指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/NALaNJ "デモを見る")**
![timimng.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/a074e51f-f416-42d0-7e8d-6f79cc418b41.gif)

プロパティの値は定義済みの値(ease、linear、ease-in等)かcubic-bezier()で指定する。

## animation-delay
アニメーションが開始するまでの遅延時間を指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/PzdJWy "デモを見る")**
![delay.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/760f9170-5ffe-2e99-1874-105df66385e8.gif)

## animation-iteration-count
アニメーションのループ回数を指定するプロパティ。
`infinite`を指定すると無限ループする。

**[デモを見る](https://codepen.io/soarflat/pen/BzOwkr "デモを見る")**
![count.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/fe081c12-c64f-d65a-f214-f9893f61059c.gif)

## animation-direction
アニメーションの再生方向を指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/dXqZvG "デモを見る")**
![direction.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/915d52ce-672c-a073-bb5b-d74991c25815.gif)

### animation-direction: normal;
アニメーションを通常再生する。(0% → 100%)
アニメーションがループの場合、再び始め(0%)から再生する。

### animation-direction: reverse;
アニメーションを逆再生する。(100% → 0%)
アニメーションがループの場合、再び逆再生の始め(100%)から再生する。

### animation-direction: alternate;
アニメーションを通常再生するが、アニメーションがループの場合
通常再生(0% → 100%) → 逆再生(100% → 0%) → 通常再生(0% → 100%) → 逆再生(100% → 0%)
といった繰り返し順で再生される。

### animation-direction: alternate-reverse;
アニメーションを逆再生するが、アニメーションがループの場合
逆再生(100% → 0%) → 通常再生(0% → 100%) → 逆再生(100% → 0%) → 通常再生(0% → 100%)
といった繰り返し順で再生される。

## animation-fill-mode
キーフレームアニメーションで指定したプロパティを
アニメーション開始前、終了後に適用するかどうかを指定するプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/YWOLbQ "デモを見る")**
![fill.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/a94c0261-3b28-136c-0a83-591292eb5093.gif)

### animation-fill-mode: none;
何も適用しない。

### animation-fill-mode: forwards;
アニメーション終了後にキーフレームアニメーションで指定したプロパティを適用する。
そのため、アニメーションが終了しても要素はアニメーション終了時(100%)の状態のままになる。

※逆再生だったら(0%)の状態のままになる。

### animation-fill-mode: backwards;
アニメーション開始前にキーフレームアニメーションで指定したプロパティを適用する。
そのため、`animation-delay`で遅延時間を指定し、アニメーションが開始していなくても
要素はアニメーション開始時(0%)の状態になる。

※逆再生だったら(100%)の状態になる。

### animation-fill-mode: both;
アニメーション開始前と終了後にキーフレームアニメーションで指定したプロパティを適用する。
`forwards`と`backwards`の両方を適用したもの。

## animation-play-state
アニメーションを再生中か一時停止にするプロパティ。

**[デモを見る](https://codepen.io/soarflat/pen/BzbrAp "デモを見る")**
![state.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/373ca6f5-f3f9-6c83-d998-80e21bafa550.gif)

## CSS Animationを利用したアニメーションのイベントを取得する
`CSS Animation`を利用したアニメーションの開始時や、終了時などのイベントをJavaScriptで取得できる。
以下はアニメーション終了時の`animationend`イベントを取得しアニメーション回数を加算しているデモ。

**[デモを見る](http://codepen.io/soarflat/pen/xOBrQN "デモを見る")**
![countainmation.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/9cd7e06c-f9c5-53c3-8d14-04a265b84139.gif)

```html:html
アニメーション回数:<span class="count"></span>
<div class="box"></div>
<input class="button" type="button" value="アニメーション開始">

```

```css:css
.box {
  width: 100px;
  height: 100px;
  background: #000;
}

.animation {
  animation: 2s ease 0s 1 normal none running animation;
}

@keyframes animation {
  0% {
    transform: scale(.3);
  }

  50% {
    transform: scale(.6) rotate(-45deg);
    background: red;
  }

  100% {
    transform: scale(1) rotate(180deg);
    background: blue;
  }
}
```

```js:JavaScript
var $box = $('.box');
var $button = $('.button');
var $count = $('.count');
var isAnimate = false;
var count = 0;

$button.on('click', function(){
  if (isAnimate) {
    return;
  }
  $button.val('アニメーション中');
  $box.addClass('animation');
  isAnimate = true;
});

$box.on('animationend', function(){
  count++;
  $button.val('アニメーション開始')
  $box.removeClass('animation');
  $count.text(count);
  isAnimate = false;
});
```

`CSS Animation`に関連するイベントは以下のものが存在する。

- animationstart
- animationend
- animationiteration

**[CSS3アニメーションのイベントを取得する](http://qiita.com/omatoro/items/0388b90a104568093691 "CSS3アニメーションのイベントを取得する")**

# CSSアニメーションを利用するうえでの注意点・知っておいた方が良いこと

## IE9以下は未対応
`CSS Transition`と`CSS Animation`はどちらもIE9以下では未対応なため、
`transition`や`animation`を指定しても動作しないため注意。

- **[CSS transitions - Can I use](http://caniuse.com/#feat=css-transitions "CSS transitions - Can I use")**
- **[CSS animations - Can I use](http://caniuse.com/#feat=css-animation "CSS animations - Can I use")**

そのため、もしIE9以下も対応する必要がある場合、
CSSではなく、JavaScriptでのアニメーションの実装を検討した方が良い。

## ベンダープレフィックスを付ける
デモではベンダープレフィックスを付けていないが、
現状、ベンダープレフィックスを付けないと動作しないブラウザもあるため`-webkit-`は必ず付けたほうが良い。

transitionの場合

```css:css
.box {
  -webkit-transition: all 300ms 0s ease;
  transition: all 300ms 0s ease;
}
```

animationの場合

```css:css
.box {
  -webkit-animation: 2s ease 0s 1 normal none running animation;
  animation: 2s ease 0s 1 normal none running animation;
}

@-webkit-keyframes animation {
  0% {
    transform: scale(.3);
  }

  50% {
    transform: scale(.6) rotate(-45deg);
    background: red;
  }

  100% {
    transform: scale(1) rotate(180deg);
    background: blue;
  }
}

@keyframes animation {
  0% {
    transform: scale(.3);
  }

  50% {
    transform: scale(.6) rotate(-45deg);
    background: red;
  }

  100% {
    transform: scale(1) rotate(180deg);
    background: blue;
  }
}
```

また、JavaScriptで取得できるイベントも、ベンダープレフィックスを付けないと
ブラウザによっては取得できないので注意。

```js:JavaScript
$('.div').on('transitionend webkitTransitionEnd', function(e) {
  // 処理
});
```

## 描画コストが低いプロパティをアニメーションに適用する
アニメーションを適用するプロパティによって描画コストが異なり、パフォーマンスに影響するため
なるべく描画コストが低い`transform`等のプロパティを適用する。

※CSSアニメーション入門の記事のためパフォーマンスに関しての詳細は割愛します。とりあえずですが、要素を動かす時は`margin`や`left`等ではなく`transform`を適用した方が良い。ということを覚えておけば良いと思います。

## Chrome DevToolsでイージングの動きを見る
Chrome DevToolsを利用すれば、設定したイージングの動きを見ることができるため、
実際にどんな動きをするのか確認しながらの実装ができる。

![easing-config.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/451bf1f0-84e1-57c1-706b-4f5509da8371.gif)

# 終わり
CSSアニメーションに関するプロパティを理解できれば、以下のようなコードも理解し易くなると思いますので
実装の参考にしたり手を加えてみるのも良いかと思います。

- **[CSSのみで実装するボタンデザインやホバーエフェクト 20＋α](http://www.nxworld.net/tips/css-only-button-design-and-hover-effects.html "CSSのみで実装するボタンデザインやホバーエフェクト 20＋α")**
- **[コピペでラクラク、押したくなるボタン用HTMLコードスニペット39個まとめ](http://photoshopvip.net/91115 "コピペでラクラク、押したくなるボタン用HTMLコードスニペット39個まとめ")**
- **[SpinKit | Simple CSS Spinners](http://tobiasahlin.com/spinkit/ "SpinKit | Simple CSS Spinners")**
- **[Single Element CSS Spinners](http://projects.lukehaas.me/css-loaders/ "Single Element CSS Spinners")**

# とりあえず基本が理解できたら
`CSS Animation`をゴリゴリ実装する場合、一読をオススメします。

- **[CSSアニメーションを使いこなすために知っておきたい5つのこと](http://qiita.com/nekoneko-wanwan/items/e8114c6e34d2950a7b28 "CSSアニメーションを使いこなすために知っておきたい5つのこと")**

パフォーマンスに関して詳しく知りたい方は以下を読めば理解が深まるかと思います。

- **[CSS プロパティのアニメーション](https://developer.mozilla.org/ja/docs/Tools/Performance/Scenarios/Animating_CSS_properties "CSS プロパティのアニメーション")**
- **[Rendering Performance](https://developers.google.com/web/fundamentals/performance/rendering/?hl=ja "Rendering Performance")**
- **[Smooth as Butter: Achieving 60 FPS Animations with CSS3](https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108#.6sp7worti "Smooth as Butter: Achieving 60 FPS Animations with CSS3")**
- **[モバイルWebのUIを速くする基本テクニックがわかる──Google I/O 2016 High Performance Web UI3](https://html5experts.jp/furoshiki/19276/ "モバイルWebのUIを速くする基本テクニックがわかる──Google I/O 2016 High Performance Web UI")**

## お知らせ

Udemy で webpack の講座を公開したり、Kindle で技術書を出版しています。

Udemy:
[webpack 最速入門](https://www.udemy.com/course/practical-webpack/?couponCode=B6D5AE3897554CBBDAD7)（**~~10,800 円~~ -> 2,000 円**）

Kindle（Kindle Unlimited だったら無料）:
[React Hooks 入門](https://www.amazon.co.jp/gp/product/B08JTXQ2M6)（500 円）

興味を持ってくださった方はご購入いただけると大変嬉しいです。よろしくお願いいたします。