react-moveableを使ってドリトライに出てくる正統後継者の福笑いもどきをつくる

# はじめに
皆さんはドリトライという漫画をご存知でしょうか。
週刊少年ジャンプで連載されていたボクシング漫画です。

全19話と短命に終わった本作ですが、登場人物の一人である虹村凶作という人物がコラ画像に使われている点で一部で有名な漫画です。

あまりにも特徴的だったので、「福笑いしたら面白そう」という動機で作りました。

試作段階なので雑な部分はありますが、以下は今回作る正統後継者福笑いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/17c1552e-d852-b7e7-bfe5-a288f55c10f1.png)

要素を移動したり、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/0285aeba-7610-de1b-5565-11b3f84fc483.png)

回転させたり、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/2e753fd1-ab0d-4c1e-5cfd-75b62cb60b2b.png)

画像を変更したり、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/55afd9d8-eed9-5d5d-d590-7728596264d3.png)

好き放題して遊びます。

# react-moveableとは
要素に拡大縮小、回転などの動きを実装できるライブラリです。

https://github.com/daybrush/moveable

# 今回のリポジトリ
ディレクトリ構成及び使用する画像です。

https://github.com/maguro-alternative/fukuwarai-moveable

# 環境構築
viteで環境構築します。この辺あたりが参考になると思います。

https://zenn.dev/longbridge/articles/93f63e0423785b

react-moveableをインストールします。
```
npm install react-moveable
```

# 実装
## App.tsx
```typescript:App.tsx
import React, { useState } from 'react';
import './App.css';

import NijimuraHana from './component/nijimuraHana.tsx';
import NijimuraKuchi from './component/nijimuraKuchi.tsx';
import NijimuraHidarime from './component/nijiruraHidarime.tsx';
import NijimuraMigime from './component/nijimuraMigime.tsx';
import NijimuraHidariMayu from './component/nijimuraHidariMayu.tsx';
import NijimuraMigiMayu from './component/nijimuraMigiMayu.tsx';
import NijimuraHidariShiwa from './component/nijimuraHidariShiwa.tsx';
import NijimuraMigiShiwa from './component/nijimuraMigiShiwa.tsx';
import NijimuraMiken from './component/nijimuraMiken.tsx';

const App = () => {
  // useState()で画像のパスを保持
  const [profileImage, setProfileImage] = useState('seitoukoukeisya.png');

  const onFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files) return;

    // React.ChangeEvent<HTMLInputElement>よりファイルを取得
    const fileObject = e.target.files[0];
    // オブジェクトURLを生成し、useState()を更新
    setProfileImage(window.URL.createObjectURL(fileObject));
  };
  return (
    <React.Fragment>
      <input type="file" accept="image/*" onChange={onFileInputChange} />
      <div style={{backgroundImage: `url('${profileImage}')`, backgroundRepeat: "no-repeat"}}>
        <NijimuraHana/>
        <NijimuraKuchi/>
        <NijimuraHidarime/>
        <NijimuraMigime/>
        <NijimuraHidariMayu/>
        <NijimuraMigiMayu/>
        <NijimuraHidariShiwa/>
        <NijimuraMigiShiwa/>
        <NijimuraMiken/>
      </div>
    </React.Fragment>
  );
};
export default App;

```

背景画像のアップロードとそれぞれのパーツを配置しています。
```backgroundImage```で```div```内の背景画像を指定しています。

## 各コンポーネント

```typescript:nijimuraMigime.tsx
import React, { useState, useEffect } from 'react';
import Moveable from 'react-moveable';

import nijimuraMigime from '/nijimuramigime.png';

const NijimuraMigime = () => {
  const [target, setTarget] = useState(null);

  useEffect(() => {
    setTarget(document.querySelector('.migime'));
  }, []);

  return (
    <>
      <div className={'moveable migime'}><img src={nijimuraMigime}/></div>
      <Moveable
        target={target}
        draggable={true}
        scalable={true}
        rotatable={true}
        origin={false}
        throttleScale={0}
        keepRatio={true}
        onDrag={e => {
          e.target.style.transform = e.transform;
        }}
        onScale={e => {
          e.target.style.transform = e.transform;
        }}
        onRotate={e => {
          e.target.style.transform = e.transform;
        }}
      />
    </>
  );
};

export default NijimuraMigime
```

```moveable migime```内の要素が操作可能になります。この中に```nijimuraMigime```から取得した画像が含まれています。
```Moveable```コンポーネントでは```useState```で指定した```target```を対象にしています。この```Moveable```コンポーネント内の
```typescript
        draggable={true}
        scalable={true}
        rotatable={true}
```
は指定された要素をドラッグ可能、サイズ変更可能、回転可能にします。
```onDrag```, ```onScale```, ```onRotate```はそれぞれ、要素がドラッグ、サイズ変更、回転されたときの処理を定義します。

これを顔のパーツごとにコンポーネントを作成していきます。

## App.css
```!important```をつけることで、要素のカスタマイズができるようになります。

```css
.moveable {
  width: 50px!important;
  height: 100px!important;
  margin: 0 0 0 auto;
}

.moveable-control {
  width: 10px!important;
  height: 10px!important;
  margin-top: -10px!important;
  margin-left: -10px!important;
}

.moveable-line {
  position: absolute!important;
  width: 1px!important;
  height: 1px!important;
  background: #000!important;
  transform-origin: 0px 0.5px!important;
}
```

```.moveable```は高さ長さを指定することで要素の判定を変更できます。
```moveable-control```は拡大、縮小、回転の際の頂点を示します。
```moveable-line```は頂点のつなぎ目になります。

他にもいろいろいじれる点があります。詳しくは以下の公式ドキュメントをご覧ください。

https://github.com/daybrush/moveable/blob/master/custom_css.md

# まとめ
かなり短くなりましたがこれで完成です。
やはり正統後継者の顔はインパクトあって、動かしているだけでも結構楽しいです。

とはいえ試作段階なので、完成したものを画像に起こすといった機能も追加して公開してみたいなと思います。