【React】シンプルなマウスストーカー（ポインター）を手軽に作成する

# 背景
マウスを動かした際の演習として、マウスのカーソルを追従するようついてくるマウスストーカーがあります。
このマウスストーカーを効果的に使用する事で、よりリッチな表現をする事ができると思います。
本記事ではReactを使用し、3つの円を使ったシンプルなマウスストーカーのサンプルについて解説します。
以下のgif画像が完成形です。

![mouse_st.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/b99e9f92-08ad-d1a6-f804-2f1788f688b1.gif)

# 環境
- vite：5.0
- React：18.2.0

# 全体のコード
先に全体のコードとCSSを以下に記載します。
```react:App.jsx
import { useEffect, useState } from "react";
import "./App.css";

const Pointer = ({ name, position }) => (
  <div
    className={`${name}`}
    style={{
      transform: `translate(${position.x}px, ${position.y}px)`,
    }}
  ></div>
);


function App() {
  // マウスの座標
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const mouseMoveListener = (event) => {
      setMousePosition({ x: event.clientX, y: event.clientY });
    };

    // マウント時：マウスイベントリスナを追加
    window.addEventListener("mousemove", mouseMoveListener);

    // アンマウント時：マウスイベントリスナを削除
    return () => {
      window.removeEventListener("mousemove", mouseMoveListener);
    };
  }, []);

  return (
    <div className="App">
      <Pointer name="pointer is-small" position={mousePosition} />
      <Pointer name="pointer" position={mousePosition} />
      <Pointer name="pointer is-large" position={mousePosition} />
    </div>
  );
}

export default App;
```
<details><summary>App.css</summary><div>

```css:App.css
body {
  padding: 0;
  margin: 0;
  background-color: aliceblue;
}

.pointer.large {
  width: 50px;
  height: 50px;
  top: -27px;
  left: -27px;
  background: rgba(0, 157, 255, 0);
  border: 2px solid rgba(0, 157, 255, 0.5);
  border-radius: 50%;
  transition: 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
}

.pointer {
  width: 40px;
  height: 40px;
  background: rgba(0, 157, 255, 0.75);
  position: fixed;
  top: -20px;
  left: -20px;
  border-radius: 50%;
  transition: 1s cubic-bezier(0.075, 0.82, 0.165, 1);
}

.pointer.small {
  width: 15px;
  height: 15px;
  top: -7.5px;
  left: -7.5px;
  background: rgba(0, 119, 255, 0.5);
  transition: 0.2s cubic-bezier(0.075, 0.82, 0.165, 1) transform;
}
```
</div></details>


<details><summary>main.jsx</summary><div>

```react:main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
```

</div></details>

# コードの詳細
以下は、ポインターを生成するコンポーネントです。
今回は３つのPointerコンポーネントを作成するので共通した1つのPointerコンポーネントを作成しておきます。
```react
const Pointer = ({ name, position }) => (
  <div
    className={`${name}`}
    style={{
      transform: `translate(${position.x}px, ${position.y}px)`,
    }}
  ></div>
);
```

Appコンポーネントでは、`mousePosition`というマウスの座標を示す状態変数を作成し、
初期値として { x: 0, y: 0 } を設定しています。

`useEffect`フックを使用し、マウント時にマウスの移動イベントリスナーを追加し、
アンマウントされたときにリスナーを削除しています。

```react
function App() {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const mouseMoveListener = (event) => {
      setMousePosition({ x: event.clientX, y: event.clientY });
    };

    window.addEventListener("mousemove", mouseMoveListener);

    return () => {
      window.removeEventListener("mousemove", mouseMoveListener);
    };
  }, []);
```

最後にPointerコンポーネントを呼び出し、
異なる3つサイズのポインターを表示しています。

```react
return (
  <div className="App">
    <Pointer name="pointer small" position={mousePosition} />
    <Pointer name="pointer" position={mousePosition} />
    <Pointer name="pointer large" position={mousePosition} />
  </div>
);
```
Pointerに当てているCSSは以下の通りです。

<details><summary>3つのPointerに当てているCSS</summary><div>

```css

.pointer.large {
  width: 50px;
  height: 50px;
  top: -27px;
  left: -27px;
  background: rgba(0, 157, 255, 0);
  border: 2px solid rgba(0, 157, 255, 0.5);
  border-radius: 50%;
  transition: 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
}

.pointer {
  width: 40px;
  height: 40px;
  background: rgba(0, 157, 255, 0.75);
  position: fixed;
  top: -20px;
  left: -20px;
  border-radius: 50%;
  transition: 1s cubic-bezier(0.075, 0.82, 0.165, 1);
}

.pointer.small {
  width: 15px;
  height: 15px;
  top: -7.5px;
  left: -7.5px;
  background: rgba(0, 119, 255, 0.5);
  transition: 0.2s cubic-bezier(0.075, 0.82, 0.165, 1) transform;
}
```
</div></details>

:::note info
Pointerが動く速度を調整する方法

今回は3つの`Pointer`を作成しました。
3つとも同じスピードで動かすよりも、
それぞれの`Pointer`が異なるスピードで動いた方が
マウスストーカーとしてはリッチに見えそうな気がします。
（個人の感想です。）

その場合は、以下のようにCSSの`transition`プロパティの秒数を変える事で、
マウスを追従するスピードを変化させる事ができます。

<details><summary>CSS</summary><div>

```css
.pointer.large {
  transition: 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
}

.pointer {
  transition: 1s cubic-bezier(0.075, 0.82, 0.165, 1);
}

.pointer.small {
  transition: 0.2s cubic-bezier(0.075, 0.82, 0.165, 1) transform;
}
```

</div></details>
:::

# 実行結果
以下のようにマウスの動きを追従するポインターを作成できました。
さらに大きさ、色、透過度等を調整する事でサイトにあったポインターを作成する事ができると思います。

![mouse_st.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/b99e9f92-08ad-d1a6-f804-2f1788f688b1.gif)