【React】四角形がたくさん表示される背景アニメーションを実装する（Anime.js）

<!-- =================================================== -->
# 概要
<!-- =================================================== -->
四角形がぶわっと表示される背景アニメーションを実装します。
アニメーションには、**Anime.js**を使用します。

![output(video-cutter-js.com) (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/53344339-0b52-9950-3e2e-80813216dd74.gif)

元ネタは以下の動画になります。

https://www.youtube.com/watch?v=M8G6SAmckxA

この動画では、バニラのHTML・CSSで実装しています。
本記事では、それをReactで実装したものになっていて、動画とは作りがかなり異なっています。

<!-- =================================================== -->
# バージョン
<!-- =================================================== -->
* react － 17.0.2
* animejs － 3.2.1
* @emotion/css － 11.1.3

<!-- =================================================== -->
# インストール
<!-- =================================================== -->
Reactプロジェクトを作成します。
プロジェクトフォルダを作成し、そこで以下を実行します。

```:cmd
npx create-react-app . --template typescript --use-npm
```

パッケージをインストールします。

```:cmd
npm i animejs
npm i -D @types/animejs
npm i @emotion/css
```

<!-- =================================================== -->
# 実装
<!-- =================================================== -->
```.tsx
import anime from 'animejs';
import React, { useEffect, useState, VFC } from 'react';
import { css, cx } from '@emotion/css';

export const BeautifulAbstractBackground: VFC = () => {
	const [refresh, setRefresh] = useState(true)

	const blockSize = 40

	const generate = () => setRefresh(!refresh)

	const randomBlockColor = () => {
		const colors = ['#fff', '#444', '#ff9213']
		const index = Math.floor(Math.random() * colors.length)
		return colors[index]
	}

	return (
		<div className={styles.container}>
			<button className={styles.button} onClick={generate}>
				Ganerate
			</button>
			{[...Array(blockSize)].map((_, i) => (
				<Block key={i} isRefresh={refresh} blockColor={randomBlockColor()} />
			))}
		</div>
	)
}

// ==============================================
// block

type BlockProps = {
	isRefresh: boolean
	blockColor: string
}

const Block: VFC<BlockProps> = props => {
	const { isRefresh, blockColor } = props

	useEffect(() => {
		const rangeX = [-window.innerWidth / 2, window.innerWidth / 2]
		const rangeY = [-window.innerHeight / 2, window.innerHeight / 2]

		anime({
			targets: '.block-anime',
			translateX: () => anime.random(rangeX[0], rangeX[1]),
			translateY: () => anime.random(rangeY[0], rangeY[1]),
			scale: () => anime.random(1, 5)
		})
	}, [isRefresh])

	return <div className={cx(styles.block(blockColor), 'block-anime')}></div>
}

// ==============================================
// styles

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		display: flex;
		justify-content: center;
		align-items: center;
		background-color: #202020;
		overflow: hidden;
	`,
	button: css`
		position: relative;
		z-index: 1;
		margin: 10px;
		border: none;
		background-color: #ce104b;
		color: #fff;
		font-size: 1.5rem;
		padding: 0.5em 1em;
		cursor: pointer;
		box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.25);
	`,
	block: (color: string) => css`
		position: absolute;
		width: 50px;
		height: 50px;
		background-color: ${color};
		box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.25);
	`
}
```

* Blockをコンポーネント化して、`blockSize`で指定した個数（40）だけBlockを生成しています。

* Blockの色は、`randomBlockColor`で3色（`#fff`・`#444`・`#ff9213`）からランダムにピックしています。

* Blockコンポーネントの中では、**anime**を使用してBlockのアニメーションを設定しています。**anime**は、`targets`でCSSセレクターによってdom要素を参照しているので、**useEffectの中で定義**します。

* アニメーションは、一番最初にコンポーネントがマウントされたタイミングと、**isRefresh**の値が変わったタイミング（[Generate]ボタンが押されたタイミング）で実行されるようにします。

`useRef`と、anime.jsの`restart`を使用することで再度アニメーションを実行することもできます。
[> refを使った実装例](https://stackoverflow.com/questions/61951482/how-to-use-animejs-inside-react/61962093)

しかしこの方法だと、アニメーションの設定で`anime.random`で設定している値が、生成されたときから変更されないので、[Generate]を押してもBlockコンポーネントの挙動が毎回同じになります。

なので、今回はuseStateで**refresh**変数を作成して、コンポーネント自体を再描画してアニメーションを再生成する処理にしています。

<!-- =================================================== -->
# 参照
<!-- =================================================== -->
Anime.jsのドキュメントは、すごくわかりやすいです。本当にすごい...

https://animejs.com/