FramerMotionでクリスマスっぽいものを作る！

# はじめに
これは[CyberAgent 20新卒 Advent Calendar 2019](https://adventar.org/calendars/3976)の7日目の記事です！
クリスマスが待ち遠しいですね🎅今回はWebフロントでクリスマスっぽい何かを作っていきたいと思います💪

# 前置き
今回はFramerMotionと言うReactのアニメーションライブラリを使ってみたいと思います！

## FramerMotionとは
[FramerMotion](https://www.framer.com/motion/)はReactのDesign系のライブラリで、リッチなアニメーションを簡単に作ることが出来ます。

```jsx
import { motion } from "framer-motion"

export const MyComponent = () => (
  <motion.div
    animate={{ scale: 2 }}
    transition={{ duration: 0.5 }}
  />
)
```
記法はこのようになっていて、アニメーションの目的地点を**animate**に、そこまでの流れを**transition**に記述することで作動します。FramerMotionでは`motion.div`のようなHTMLの要素に加え、`motion.svg`でsvgに対しても簡単にアニメーションを付与することが可能です！

> 指定可能なanimationの参考
https://www.framer.com/api/motion/component/#supported-values  
  
> 指定可能なtransitionの参考
https://www.framer.com/api/motion/types/#transition  

# 作業内容

## 概要

今回はFramerMotionのsvgアニメーションに着目して、svgでクリスマスツリーのアニメーションを作ってみたいと思います！それでは順番に解説していきます！

## 手順
1. イラストをSVGで書き出す
2. Reactプロジェクト作成とSVGの埋め込み
3. FramerMotionでアニメーションをつける

（Option: 雪を降らせる❄️）

### 1. イラストをSVGで書き出す
SVGは座標指定することで図形を描画できます。
> 参考
https://developer.mozilla.org/ja/docs/Web/SVG/Tutorial/Paths

ですが、複雑な図形を作るのは大変なので、Drawingソフトで図形を描き、自動でSVGをエクスポートします。今回は、無料で使える[Vectr](https://vectr.com/)を使って作成しました！
🚨デザインは一筆書きで作ります。

### 2. Reactプロジェクト作成とSVGの埋め込み
#### プロジェクト作成
FramerMotionはReact用のDesignライブラリなので、Reactのプロジェクトを新規で作成します。今回は`create-react-app`を使ってサクッと立ち上げていきます！

```console
// Reactプロジェクト作成
$ yarn create react-app merry-xmas
// framer-motionの追加
$ yarn add framer-motion
```
今回は

* ツリーてっぺんの星
* 木の部分（緑色）
* 幹の部分（茶色）

でパーツを作って組み合わせます。

#### SVG埋め込み
1.でエクスポートした.svgのファイルをVSCodeなどで開くと、`<path d="M...Z">`が見つかると思います。この`" "`で囲まれた中をFramerMotionの`<motion.path>`に記述することで、線画のアニメーションが可能となります👍

```jsx
// 木の部分（緑色）
<motion.path
  className="tree-body-stroke"
  d="M140.93 32.47L154.78 49.39L168.57 66.88L177.28 76.47L147.28 76.47L156.16 90.01L167.44 103.9L183.23 123.85L199.78 140.21L213.13 150.93L177.28 150.93L184.78 164.46L197.9 182.65L217.08 205.15L234 222L0.48 222L19.78 202.26L42.28 174.05L58.57 150.93L21.35 150.93L32.63 141.9L46.73 127.8L63.09 108.62L76.06 92.65L87.28 75.91L57.28 75.91L73.8 57.29L88.47 38.11L117.28 -0.24L140.93 32.47Z"
  variants={body}
  initial="hidden"
  animate={isVisible ? "visible" : "hidden"}
/>
```

上記の`variants`はより柔軟なアニメーションの指定ができる仕組みだと思います。

```jsx
const body = {
  hidden: {
    // animateの内容
    opacity: 0,
    pathLength: 0,
    fill: "rgba(2, 135, 96, 0)",
    // transitionの内容
    transition: {
      duration: 3.3,
      ease: "anticipate",
      fill: { duration: 3.5 }
    }
  },
  visible: {
    opacity: 1,
    pathLength: 1,
    fill: "rgba(2, 135, 96, 1)",
    transition: {
      duration: 2,
      ease: "easeInOut",
      fill: { duration: 3 }
    }
  }
};
```

こんな感じでanimateとtransitionを個別の条件ごとに定義できます。

### 3. FramerMotionでアニメーションをつける
今回は

* Springによるアニメーション
* ドローのアニメーション

の2つを作っていきます。

#### Springによるアニメーション
Springとは、バネのような物理的な挙動を模したアニメーションです。他のライブラリだと[react-spring](https://www.react-spring.io/)が有名です。

今回はこれで、左右にバウンドする星を作ってみたいと思います！

```jsx
<motion.svg
  xmlns="https://www.w3.org/2000/svg"
  viewBox="0 0 50 50"
  initial={{ rotate: 0 }}
  animate={{
    rotate: [-45, 40, -35, 30, -25, 20, -15, 10, -5, 3, -2, 1, 0]
  }}
  transition={{
    delay: 1.5,
    type: "spring",
    loop: Infinity,
    repeatDelay: 2,
    duration: 1.3
  }}
>
```

徐々に減衰していく運動を、今回は簡単に`rotate`を減少させる形で実現しました。これでプルプル揺れるお星様の完成です⭐️

#### ドローのアニメーション
SVGは座標をつなぎ合わせて図形を描画しており、この**つなぎ合わせる部分**をアニメーションにすることが出来ます！今回はこれで、ツリーの外形が徐々に作られていく様子をアニメーションにしたいと思います。

```jsx
const body = {
  hidden: {
    pathLength: 0,
    fill: "rgba(2, 135, 96, 0)",
    ...
  },
  visible: {
    pathLength: 1,
    fill: "rgba(2, 135, 96, 1)",
    ....
  }
};
```

`pathLength`が1で全ての点が結合された（図形が描画された）状態になります。
`fill`で枠の内側の着色を指定できます。

# 完成
最終的に出来上がったものが[こちら](https://dai7igarashi.github.io/Xmas-tree/)になります🎄

![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263675/ec79a643-271c-2c87-8c3d-f8e70c29c3ab.gif)

*メリークリスマスイブ×18日*

# まとめ
今回初めてAdventCalendarに投稿させていただきましたが、固すぎず緩すぎず、ちょうど良い温度感で取り組めたのが良かったなと思っています！お誘いいただきありがとうございました👍

自分はデザインの方面にも興味があるので、今後もこうしたデザイン系の情報をキャッチアップしていければなと思います！

##### 最後に
☃️雪は[particle.js](https://vincentgarreau.com/particles.js/#snow)で描画しています❄️