【React】Qiita アドカレみたいな雪が降るアニメーションを作る

## はじめに
[Qiita Advent Calendar 2021](https://qiita.com/advent-calendar/2021/qiita)の2日目は、
Qiita 株式会社 プロダクト開発G デザインTMの出口（@degudegu2510）が担当します！
業務では、[Qiita](https://qiita.com/)、[Qiita Team](https://teams.qiita.com/) 、[Qiita Jobs](https://jobs.qiita.com/)のUI/UX、フロントエンド、各イベント等のクリエイティブを担当しています。

今年から大きくリニューアルされたQiita Advent Calendar 2021のデザインを担当したのが僕です。
リニューアルした内容や、どうしてこのようなデザインにしたのかををまとめた記事も投稿しています。
ぜひご覧ください。

https://qiita.com/degudegu2510/items/52cd6ed8737783362bf7

この記事では、リニューアルされたQiita Advent Calendar 2021 の[TOPページ](https://qiita.com/advent-calendar/2021)の雪が降るアニメーションについての記事です。

+ Qiita Advent Calendar みたいな雪を降るアニメーションをつくってみたい。
+ particles.jsについて知りたい。

という人におすすめです。

## particles.js

particles.jsは、雪っぽいアニメーションや宇宙っぽいアニメーション、幾何学図形をアニメーションさせたりできるJSのライブラリーになります。

|雪|宇宙|幾何学図形|
|----|----|----|
|![スクリーンショット 2021-11-22 19.58.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/911a81db-62dd-eae9-8cb8-8d7cfc7d2b90.png)|![スクリーンショット 2021-11-22 19.59.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/3175b891-e65f-f112-6d9b-3da6a2375973.png)|![スクリーンショット 2021-11-22 20.00.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/39cfdb2d-bc29-7b1f-5956-8bbe9998f2c0.png)|


パラメーターが豊富で、マウスのホバーに合わせてアニメーションさせたり、
クリックに合わせてアニメーションさせたり、色々なカスタマイズが可能です。

公式デモサイトで、パラメーターをいじりながらアニメーションを試すことができます。
(公式デモサイトをいじってるだけで楽しいです。)

https://vincentgarreau.com/particles.js/#snow

## インストール
react-particles-jsをインストールします。
※ npmを使用する場合は、以下のコマンドでインストールできます。

```cmd
npm i react-particles-js
```

## 実装
コーディングは簡単です。最小で実装するとこれだけ。

```tsx
import css from '@emotion/css'
import React from 'react'
import Particles from 'react-particles-js'

export const AdventCalendarHeroAnimation = () => {
    return (
        <Particles
            params={{
                {/* ここでアニメーションのパラメータを設定しています */}
                particles: {
                    number: {
                        value: 20,
                        density: {
                            enable: true,
                            value_area: 2000,
                        },
                    },
                   ... {/* パラメーターの設定が続く */}
                },
            }}
        />
    )
}
```

## 各パラメーターについて
https://vincentgarreau.com/particles.js/#default

こちらのURLでパラメーターの値を変えることでどんなアニメーションになるか確認できます。
ここでは、各パラメーターが何が変わるかを説明します。
### particles
|number| | |
|----|----|----|
|value| 画面内のシェイプの数| number |
| density.enable | valueの数を表示するareaを決めるか | bool |
| density.value_area | valueの数を表示するをareaの大きさ | number |

|color| | |
|----|----|----|
|value| シェイプの色 | string |

|shape| | |
|----|----|----|
|type|shapeの形| circle、edge、triangle <br> polygon、star、image|
|stroke.width| 枠線の太さ | number |
|stroke.color| 枠線の色 | srting |
|polygon.nb_sides|typeがpolygonの時の頂点の数| number |
| image.src |typeがimageの時の画像| string |
|image.width|typeがimageの時の画像の横幅|number|
|image.height|typeがimageの時の画像の縦幅|number|

|size| | |
|----|----|----|
|value| シェイプの大きさ| number |
|random| 大きさをランダムにするか|bool|
|anim.enable|大きさをアニメーションさせるか|bool|
|anim.speed|アニメーションのスピード|number|
|anim.size_min|アニメーション時のシェイプの最小の大きさ|number|
|anim.sync|全てのシェイプのアニメーションを合わせるか|bool|

|opacity| | |
|----|----|----|
|value| シェイプの不透明度| number |
|random| 不透明度をランダムにするか|bool|
|anim.enable|不透明度をアニメーションさせるか|bool|
|anim.speed|アニメーションのスピード|number|
|anim.size_min|アニメーション時のシェイプの最小の不透明度|number|
|anim.sync|全てのシェイプのアニメーションを合わせるか|bool|

|line_linked| | |
|----|----|----|
|enable_auto| シェイプ間の線を引くか|bool|
|distance|シェイプ間の線を引く時のシェイプ間の距離|number|
|color|シェイプ間の線の色|string|
|opacity|シェイプ間の線の不透明度<br>(シェイプの不透明度に影響されます)| number|
|width|シェイプ間の線の幅|number|

|move| | |
|----|----|----|
|enable|シェイプをアニメーションさせるか|bool|
|direction|シェイプのアニメーション方向|none、top、top-right、right、bottom-right、bottom、bottom-left、left、top-left|
|random|シェイプのアニメーションスピードをランダムにするか|bool|
|speed|シェイプのアニメーションスピード|string|
|straight|シェイプのアニメーションを真っ直ぐにするか|bool|
|out_mode|画面端に当たった時にどうするか|out、bounce|
|attract.enable|画面外に出たら消すか|bool|
|attract.rotateX|何が変わってるのかわからない|number|
|attract.rotateY|何が変わってるのかわからない|number|

### interactivity
|onhover| | |
|----|----|----|
|enable|マウスホバーでアニメーションするか| bool|
|mode|マウスホバーアニメーションのモード| repuse、grab、bubble|

|onClick| | |
|----|----|----|
|enable|クリックでアニメーションするか| bool|
|mode|クリックアニメーションのモード| push、remove、repuse、bubble|

|modes| | |
|----|----|----|
|grab.distance|grabモードでシェイプ間の線を引く時のシェイプ間の距離| number|
|grab.line_linked.opacity|grabモードのシェイプ間の線の不透明度| number|
|bubble.distance|bubbleモードで変化させる距離|number|
|bubble.size|bubbleモードで変化させる大きさ|number|
|bubble.opacity|bubbleモードで変化させる不透明度|number|
|bubble.duration (sec)|bubbleモードで変化させる時間|number|
|bubble.repulse|repulseモードで変化させる距離|number|


### page background
|page background|||
|----|----|----|
|background-color|背景色|string|
|background-image|背景画像|string|
|background-position|背景画像の場所|string|
|background-repeat|背景画像のrepeat設定|string|

## Qiita Advent Calendar Topページの実装
Qiita Advent CalendarのTopページでは、このように実装しています。

### 完成形
![スクリーンショット 2021-11-30 9.57.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/d04f93d1-72e2-dfb2-2653-fbe088bc4236.png)
動いている様子は[こちら](https://qiita.com/advent-calendar/2021)からご確認ください



### コード
 **マークアップ部分** 

```tsx
import css from '@emotion/css'
import React from 'react'
import Particles from 'react-particles-js'

export const AdventCalendarHeroAnimation = () => {
  {/*表示領域を取得するライブラリを使用しています。直接値を入れても使えます。*/}
  const { currentWidth, currentHeight } = useWindowSize()

  return (
    <div css={heroStyle(heroBackgroundImage)}>
        {/* Qiita Advent Calendar の ロゴを入れます */}
        <img src={'./logo-advent_calendar.svg'} css={heroAnimationLogoStyle} />
        {/* react-particles-jsで雪を表現しています */}
        <Particles
            width={`${currentWidth}px`}
            height={`${currentHeight}px`} 
            style={{ display: 'block' }}
            params={{
                particles: {
                    number: {
                        value: 20,
                        density: {
                            enable: true,
                            value_area: 2000,
                        },
                    },
                    size: {
                        value: 8,
                        random: true,
                    },
                    opacity: {
                        anim: {
                            enable: false,
                        },
                        value: 1,
                        random: false,
                    },
                    move: {
                        direction: 'bottom',
                        out_mode: 'out',
                        speed: 6,
                        random: false,
                    },
                    line_linked: {
                        enable: false,
                    },
                },
            }}
        />
        {/* 画面下にある丘の画像を入れます */}
        <img src={'./image-back_hill.svg'} css={heroHillStyle} />
        <img src={'./image-front_hill.svg'} css={heroHillStyle} /> 
    </div>
  )
}
```

**スタイリング部分** 

``` tsx
const heroStyle = (heroBackgroundImage: string) =>
  css({
    backgroundImage: `url(${heroBackgroundImage: string})`, // 背景画像を設定しています。
    backgroundSize: 'contain',
    position: 'relative',
    width: '100%',
  })

const logoAnimation = keyframes({
  '0%': {
    opacity: 0,
    transform: 'translate(-50%, -100%) scale(1.5)',
    ...viewportXS({
      transform: 'translate(0, 0) scale(1.5)',
    }),
  },
  '100%': {
    opacity: 1,
    transform: 'translate(-50%, -100%) scale(1)',
    ...viewportXS({
      transform: 'translate(0, 0) scale(1)',
    }),
  },
})

const heroAnimationLogoStyle = css({
  animationDelay: '0s',
  animationDuration: '3s',
  animationName: logoAnimation,
  left: '50%',
  maxWidth: 752,
  padding: `0 ${getSpace(2)}px`,
  position: 'absolute',
  top: '50%',
  transform: 'translate(-50%, -100%)',
  width: '100%',
  zIndex: 10,
})


const heroHillStyle = css({
  bottom: 0,
  left: '50%',
  minWidth: 1280,
  position: 'absolute',
  transform: 'translateX(-50%)',
  width: '100%',
  zIndex: 10,
  filter: 'drop-shadow(0px 4px 16px rgba(0, 0, 0, 0.5))',
})
```

## まとめ

この記事では、リニューアルされたQiita Advent Calendar 2021 の[TOPページ](https://qiita.com/advent-calendar/2021)の雪が降るアニメーションについてとparticles.jsについて説明しました。

particles.jsは、導入するのも実装するのも簡単でした。
特に、↓このサイトでパラメーターの設定を確認してから、実装できるので、
実装しながらパラメーターを調整することがほとんどなく、スムーズに実装できました。

雪のアニメーションだけではなく、
シェイプを桜の花びらにして、花びらが舞っているみたいなアニメーションも面白そうだなと思います。

https://vincentgarreau.com/particles.js/#default

---

最後まで読んでくださってありがとうございます！

[Qiita Advent Calendar 2021](https://qiita.com/advent-calendar/2021/qiita)の3日目も、
Qiita 株式会社 プロダクト開発G デザインTMの出口（@degudegu2510）が担当します！

ぜひ[Qiita Advent Calendar 2021](https://qiita.com/advent-calendar/2021/qiita)を購読設定して、
明日の記事もご覧いただけると嬉しいです。

また、デザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510