React + TypeScript: Framer Motionでアニメーションを加える

[Framer Motion](https://www.framer.com/motion/)は、本番環境に対応したReact用のモーションライブラリです。構文は宣言的で、複雑なアニメーションのコードも簡潔に書けます。つまり、コードベースが読みやすく、保守しやすいということです。

Framer Motionは、つぎのようにインストールしてください。

```terminal
npm install framer-motion
```

# アニメーションの基本
宣言的な構文でアニメーションのコードを書くために、中心的な役割を果たすのが[`motion`](https://www.framer.com/motion/component/)コンポーネントです。DOM要素として扱え、アニメーションの機能が拡張されます。

たとえば、アニメーションさせたい要素が`<div />`であれば、`motion`コンポーネントを`import`したうえで、つぎのように`<motion.div />`に差し替えてください。

```react:src/App.tsx
import { motion } from 'framer-motion';

export default function App() {
	return (
		// <div />
		<motion.div />
	);
}
```

`motion`コンポーネントにアニメーションを加えるのは[`animate`](https://www.framer.com/motion/component/###animate)プロパティです。値をオブジェクトでつぎのように定めれば、コンポーネントが表示されると、サイズ(`scale`)は半分になりつつ、透明(`opacity`)に変化します。

```react:src/App.tsx
import { motion } from 'framer-motion';

export default function App() {
	return (
		<motion.div
			animate={{ opacity: 0, scale: 0.5 }}
		/>
	);
}
```

アニメーションが速すぎるかもしれません。どう変化させるかを調整するのは、[`transition`](https://www.framer.com/motion/component/###transition)プロパティです(「[Transition](https://www.framer.com/motion/transition/)」参照)。[`duration`](https://www.framer.com/motion/transition/###duration)でアニメーションにかける秒数を定めましょう。

```react:src/App.tsx
export default function App() {
	return (
		<motion.div
			animate={{ opacity: 0, scale: 0.5 }}
			transition={{ duration: 1 }}
		/>
	);
}
```

[`initial`](https://www.framer.com/motion/component/###initial)プロパティを用いれば、アニメーションの初期値が決められます。DOM(`<motion.div />`)のもともとの設定と異なる初期値から、本来の表示に戻るよう変化させる例がつぎのコードです(サンプル001)。透明な(`opacity`)要素が左(`x`)から姿を表し、拡大(`scale`)しながら、もともとの設定に落ち着きます。

```react:src/App.tsx
export default function App() {
	return (
		<motion.div
			initial={{ opacity: 0, scale: 0, x: -400 }}
			// animate={{ opacity: 0, scale: 0.5 }}
			animate={{ opacity: 1, scale: 1, x: 0 }}
			transition={{ duration: 1 }}
		/>
	);
}
```

**サンプル001■React + TypeScript: Framer Motion Examples 01**
https://codesandbox.io/s/react-typescript-framer-motion-examples-01-gfy2zq


# キーフレーム
`animate`に与えるオブジェクトプロパティの値を配列で渡して、段階的に変化させるのが[キーフレーム](https://www.framer.com/motion/animation/##keyframes)です。以下のコード例は、つぎの3つのプロパティを開始値から終了値に変化させます。

| プロパティ | 開始値 | 終了値 |
|:-:|:-:|:-:|
| scale | 2 | 1 |
| rotate | 180 | 0 |
| borderRadius | 50% | 0% |

```react:src/App.tsx
export default function App() {
	return (
		<motion.div
			animate={{
				scale: [2, 1],
				rotate: [180, 0],
				borderRadius: ['50%', '0%']
			}}
			transition={{
				duration: 2
			}}
		/>
	);
}
```

プロパティ値(キーフレーム)は、配列要素にいくつ加えても構いません。そうすると、細かなアニメーションが定められるでしょう。

```react:src/App.tsx
export default function App() {
	return (
		<motion.div
			animate={{
				// scale: [2, 1],
				scale: [1, 2, 2, 1, 1],
				// rotate: [180, 0],
				rotate: [0, 0, 180, 180, 0],
				// borderRadius: ['50%', '0%']
				borderRadius: ['0%', '0%', '50%', '50%', '0%']
			}}

		/>
	);
}
```

デフォルトでは、[キーフレーム間のアニメーション時間は均等](https://www.framer.com/motion/animation/##keyframes:~:text=Each%20keyframe%20will%20be%20spaced%20evenly%20throughout%20the%20animaton.)です。`transition`の[`times`](https://www.framer.com/motion/transition/###times)で、この間隔が調整できます(サンプル002)。値は0が開始、1で終了する小数値です。さらに、[`ease`](https://www.framer.com/motion/transition/###ease)はイージング関数、[`repeat`](https://www.framer.com/motion/transition/###repeat)が繰り返し回数、[`repeatDelay`](https://www.framer.com/motion/transition/###repeatdelay)でつぎの繰り返しまでの溜め(待ち秒数)を定められます。

```react:src/App.tsx
export default function App() {
	return (
		<motion.div

			transition={{
				duration: 2,
				ease: 'easeInOut',
				times: [0, 0.2, 0.5, 0.8, 1],
				repeat: Infinity,
				repeatDelay: 1
			}}
		/>
	);
}
```

**サンプル002■React + TypeScript: Framer Motion Examples 02**
https://codesandbox.io/s/react-typescript-framer-motion-examples-02-8k925n


# ジェスチャーアニメーション
[ジェスチャー](https://www.framer.com/motion/examples/#gesture-animations)は、アクティブになるとその間コンポーネントの視覚的な状態にアニメーションを加えるつぎのようなヘルパープロパティです。

* [`whileHover`](https://www.framer.com/motion/gestures/###whilehover)
* [`whileTap`](https://www.framer.com/motion/gestures/###whiletap)
* [`whileDrag`](https://www.framer.com/motion/gestures/###whiledrag)
* [`whileFocus`](https://www.framer.com/motion/gestures/###whilefocus)

つぎのコード例では、`whileHover`と`whileTap`を用いました。値のアニメーションの定め方は、`animate`と同じです。以下のサンプル003で、コードと動きを見比べていただくのが手っ取り早いでしょう。

```react:src/App.tsx
export default function App() {
	return (
		<div>
			<motion.div whileHover={{ scale: 1.2 }} whileTap={{ scale: 0.8 }} />
		</div>
	);
}
```

**サンプル003■React + TypeScript: Framer Motion Examples 03**
https://codesandbox.io/s/react-typescript-framer-motion-examples-03-dfgpq4


# ドラッグ
ドラッグのジェスチャーアニメーションは、[`drag`](https://www.framer.com/motion/gestures/#drag)プロパティを加えるだけです。ドラッグ領域の上(`top`)下(`bottom`)左(`left`)右(`right`)の範囲は、[`dragConstraints`](https://www.framer.com/motion/gestures/###dragconstraints)プロパティで決められます。

```react:src/Example.tsx
export const Example: FC = () => {
	return (
		<>
			<motion.div
				drag
				dragConstraints={{
					top: -50,
					left: -50,
					right: 50,
					bottom: 50
				}}
			/>
		</>
	);
};
```

ドラッグ領域を数値でなく、DOM要素の矩形で定めたい場合もあるでしょう。そのときは、範囲にしたいDOM要素に`ref`を与えてください。`dragConstraints`プロパティの値とするのはその`ref`オブジェクトです(サンプル004)。

```react:src/Example.tsx
export const Example: FC = () => {
	const constraintsRef = useRef<HTMLDivElement>(null);
	return (
		<>
			<motion.div ref={constraintsRef} />
			{/* <motion.div
				drag
				dragConstraints={{
					top: -50,
					left: -50,
					right: 50,
					bottom: 50
				}}
			/> */}
			<motion.div drag dragConstraints={constraintsRef} />
		</>
	);
};
```

**サンプル004■React + TypeScript: Framer Motion Examples 04**
https://codesandbox.io/s/react-typescript-framer-motion-examples-04-3rnffz

Framer Motionの基本的な使い方について、作例のコードを書き進めながらご説明しました。作例は公式サイトの「[Examples](https://www.framer.com/motion/examples/)」から拾いつつ、細かな手を加えています。

「Examples」には、本稿で説明しきれなかったサンプルがまだ多くあるので、興味のある方はご覧になるとよいでしょう。もっとも、コードの説明はほぼなく、サンプルにも不要なモジュールやコードが残っていたりするのでご注意ください。

この記事が好評でしたら、続編も考えます。