【React】マウス位置だけ焦点（Focus）の合う背景を実装する

<!-- =========================================================== -->
# 概要
<!-- =========================================================== -->
**ぼやけた背景に、マウスをあてたところだけ焦点の合う背景**を実装します。

![output(video-cutter-js.com) (11).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/ba93a992-0e47-e7be-c1f9-872e68c3de2e.gif)


:::note info
本記事は、以下の動画を<strong>React</strong>で実装したものです。
:::

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

:::note info
スタイリングには、<strong>emotion/css</strong>（CSS in JS）を使用しています。
:::

https://emotion.sh/docs/@emotion/css

https://qiita.com/nemutas/items/dac73aa645f27cde92cd


<!-- =========================================================== -->
# 実装
<!-- =========================================================== -->
実装のイメージ図です。
画像を2枚重ねて、下の画像は**blur**をかけ、上の画像は**clip-path**を使用して一部だけ見えるようにします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/3192e2a6-3131-2962-3b6a-5ab0f7e53c2c.png" width="500">

```.tsx
import React, { useRef, VFC } from 'react';
import { css } from '@emotion/css';

export const FocusBlur: VFC = () => {
	const containerRef = useRef<HTMLDivElement>(null)

	const mousemoveHandler = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
		containerRef.current!.style.setProperty('--x', e.clientX + 'px')
		containerRef.current!.style.setProperty('--y', e.clientY + 'px')
	}

	return (
		<div ref={containerRef} className={styles.container} onMouseMove={mousemoveHandler}>
			<div className={styles.box}>
				<h2 className={styles.text}>Forcus</h2>
			</div>
			<div className={styles.box}>
				<h2 className={styles.text}>Forcus</h2>
			</div>
			<div className={styles.circle}></div>
		</div>
	)
}

const templates = {
	flex: css`
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	image: css`
		background-image: url('/assets/bg.jpg');
		background-size: cover;
		background-position: center;
		background-attachment: fixed;
	`
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		overflow: hidden;
		${templates.flex}
		${templates.image}
	`,
	box: css`
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		${templates.flex}
		${templates.image}

		&:nth-child(1) {
			filter: blur(10px);
		}
		&:nth-child(2) {
			clip-path: circle(150px at var(--x) var(--y));
		}
	`,
	text: css`
		position: absolute;
		color: #fff;
		font-size: 10rem;
		user-select: none;
		transform: translate(calc(var(--x) / 25), calc(var(--y) / 25));
	`,
	circle: css`
		position: absolute;
		top: -150px;
		left: -150px;
		width: 300px;
		height: 300px;
		border: 2px solid #fff;
		border-radius: 50%;
		box-shadow: 0px 5px 25px rgba(0, 0, 0, 0.25);
		transform: translate(var(--x), var(--y));
	`
}
```
* **templates**で、共通に使えるCSSをまとめています。

* **setProperty**を使用して、マウス位置をCSS変数`--x`・`--y`に格納しています。
CSS変数のスコープを、このコンポーネントだけにしたいので、**containerRef**に割り当てています。

```.tsx
const mousemoveHandler = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
	containerRef.current!.style.setProperty('--x', e.clientX + 'px')
	containerRef.current!.style.setProperty('--y', e.clientY + 'px')
}
```
https://developer.mozilla.org/ja/docs/Web/CSS/Using_CSS_custom_properties

https://developer.mozilla.org/ja/docs/Web/API/CSSStyleDeclaration/setProperty