概要
四角形がぶわっと表示される背景アニメーションを実装します。
アニメーションには、Anime.jsを使用します。
元ネタは以下の動画になります。
この動画では、バニラのHTML・CSSで実装しています。
本記事では、それをReactで実装したものになっていて、動画とは作りがかなり異なっています。
バージョン
- react - 17.0.2
- animejs - 3.2.1
- @emotion/css - 11.1.3
インストール
Reactプロジェクトを作成します。
プロジェクトフォルダを作成し、そこで以下を実行します。
npx create-react-app . --template typescript --use-npm
パッケージをインストールします。
npm i animejs
npm i -D @types/animejs
npm i @emotion/css
実装
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を使った実装例
しかしこの方法だと、アニメーションの設定でanime.random
で設定している値が、生成されたときから変更されないので、[Generate]を押してもBlockコンポーネントの挙動が毎回同じになります。
なので、今回はuseStateでrefresh変数を作成して、コンポーネント自体を再描画してアニメーションを再生成する処理にしています。
参照
Anime.jsのドキュメントは、すごくわかりやすいです。本当にすごい...