Next.js(React)でアイコンのリサイズ機能をサクッと実装する方法

Next.js(React)でアイコンのリサイズ機能をサクッと実装する方法

Next.jsTypeScriptReact

おはこんばんは。最近、Next.js(React)でアイコンのリサイズ機能を実装してみたのですが、意外とまとまった記事が無かったので実装方法をご紹介しようと思います!

今回はこんな感じのを実装します。サンプルはこちら。

blog image

実行環境

  • next 10.0.9
  • react 17.0.1
  • react-modal 3.12.1
  • react-avater 3.10.0
  • react-avater-editor 11.1.0
  • rc-slider 9.7.1
  • pica 6.1.1

機能説明

今回の機能は主に以下となります。これを順に紹介していきます。

  • アイコンの表示
  • 画像のアップロード
  • アイコンのプレビュー表示
  • アイコンのサイズ・切り取り範囲の変更

アイコンの表示

まずは、最初の画面でアイコンを表示できるようにします。今回はreact-avaterを利用します。画像が無い際のデフォルト表示や画像の円形表示をしてくれるお手軽ライブラリです。

1yarn add react-avatar

次に表示用とプレビュー用のStateを定義します。このStateで画像を管理します。

./page/index.tsx
1const [icon, setIcon] = useState<File | null>(null);
2const [previewIcon, setPreviewIcon] = useState<File | null>(null);

そして以下みたいに利用すれば表示してくれます。ただ、まだ画像が無いので、デフォルト表示になるかと思います。

./page/index.tsx
1<Avatar
2 size='160'
3 name="アイコン"
4 round
5 color="#ddd"
6 alt="アイコン"
7 src={icon ? URL.createObjectURL(icon) : ''}
8/>

画像のアップロード

では、早速画像のアップロードをできるようにします。今回はボタンを押されたら画像がアップロードされるようにします。

ページ上に表示されない input を以下のように定義し、ref を取得出来るようにします。

./page/index.tsx
1const iconInputRef = useRef<HTMLInputElement | null>(null);
2
3<input
4 type="file"
5 accept="image/*"
6 style={{ display: 'none' }}
7 ref={iconInputRef}
8 onChange={handleChangePreviewIcon}
9/>

先ほど、取得した inputのref がボタンのクリックイベントでクリックされるようにします。これで画像選択が可能になります。

./page/index.tsx
1const handleClickChangeIcon = useCallback(() => {
2if (!iconInputRef || !iconInputRef.current) return;
3iconInputRef.current.click();
4}, []);
5
6<button
7 type="button"
8 onClick={handleClickChangeIcon}
9>
10

最後にプレビューのStateを選択された画像を更新するようにします。e.currentTarget.value = "''をしているのは、同じ画像を連続で選択された際、イベントが実行されないので、valueをリセットして再実行可能とするためです。

./page/index.tsx
1const handleChangePreviewIcon = useCallback(
2 (e: ChangeEvent<HTMLInputElement>) => {
3 if (!e.target.files?.length) return;
4 setPreviewIcon(e.target.files[0]);
5 e.currentTarget.value = '';
6 },
7 [],
8);

アイコンのプレビュー表示

プレビューの表示では、react-modalを利用します。お手軽にモーダルが実装できるライブラリです。

1yarn add react-modal

previewIconに値がセットされたらモーダルが開くようにしています。

./components/IconEditor.tsx
1<Modal
2 isOpen={!!previewIcon}
3 onRequestClose={handleCloseIsOpen}
4 ariaHideApp={false}
5 overlayClassName={{
6 base: styles.overlayBase,
7 afterOpen: styles.overlayAfter,
8 beforeClose: styles.overlayBefore,
9 }}
10 className={{
11 base: styles.contentBase,
12 afterOpen: styles.contentAfter,
13 beforeClose: styles.contentBefore,
14 }}
15 closeTimeoutMS={500}
16>
17 <div className={styles.header}>
18 </div>
19</Modal>

アイコンのサイズ・切り取り範囲の変更

画像のリサイズにはreact-avater-editorrc-sliderを利用します。

1yarn add react-avater-editor rc-slider

AvatarEditorではrefを渡すことでリサイズした画像データを取得出来ます。scaleで画像の拡大が可能です。その調整はrc-sliderを用いて行います。

./components/IconEditor.tsx
1const editorRef = useRef<AvatarEditor | null>(null);
2const [scale, setScale] = useState(1);
3
4<AvatarEditor
5 ref={editorRef}
6 image={previewIcon ? URL.createObjectURL(previewIcon) : ''}
7 width={ICON_WIDTH}
8 height={ICON_HEIGHT}
9 borderRadius={100}
10 color={[0, 0, 0, 0.6]}
11 scale={scale}
12 rotate={0}
13/>
14
15<Slider
16 onChange={handleChangeScale}
17 min={1}
18 max={1.5}
19 step={0.01}
20 value={scale}
21/>
22

ただ、AvatarEditorのwidthとheightがコンポーネントのサイズになるのですが、これが実際にリサイズされる画像の大きさになります。これを必ずしもそうしたくないケースがありますよね。そういった場合は、picaを利用します。

1yarn add pica

1 const handleClickFileSave = useCallback(async () => {
2 if (!editorRef.current) return;
3
4 const img = editorRef.current.getImage();
5 const canvas = editorRef.current.getImageScaledToCanvas();
6 canvas.width = ICON_WIDTH;
7 canvas.height = ICON_HEIGHT;
8 const picaCanvas = await pica().resize(img, canvas, { alpha: true });
9
10 picaCanvas.toBlob((blob) => {
11 const nextFile = new File([blob], previewIcon.name, {
12 type: previewIcon.type,
13 lastModified: Date.now(),
14 });
15 onChangeIcon(nextFile);
16 handleCloseIsOpen();
17 });
18
19 }, [previewIcon, onChangeIcon]);

これで期待する画像にリサイズが出来ます。お疲れ様でした。

さいごに

今回は、Next.jsでアイコンのリサイズ機能を実装する方法をご紹介しました!Reactは色々とライブラリがあって便利ですね。今回の記事が誰かの参考になれば幸いです。