🔎

react-image-cropを使って、プロフィールの丸い画像を作ってみる

2022/11/06に公開約3,600字

色々探してみて以下2つが良さそうだと感じました。

https://www.npmjs.com/package/react-image-crop
https://www.npmjs.com/package/react-cropper

Weekly Downloadsを見てみると、react-image-cropの方がダウンロード数が多そうなので、今回はこちらを使用します。

作ってみたもの

まず画像をアップロードして、切り取りたいところを選択して、切り取りボタンを押すと、

選択部分を切り取れるものになります。

ソースコード

import React, { ChangeEvent, FormEvent, useEffect, useState } from "react";
import ReactCrop from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";
import { Crop } from "react-image-crop/dist/types";

const CropImg = () => {
  const [fileData, setFileData] = useState<File | undefined>();
  const [objectUrl, setObjectUrl] = useState<string | undefined>();

  //プロフィールイメージ
  const [profileImg, setProfileImg] = useState<string>("");

  //Crop
  const [crop, setCrop] = useState<Crop>({
    unit: "px", // 'px' または '%' にすることができます
    x: 0,
    y: 0,
    width: 200,
    height: 200,
  });

  //アップロードした画像のObjectUrlをステイトに保存する
  useEffect(() => {
    if (fileData instanceof File) {
      objectUrl && URL.revokeObjectURL(objectUrl);
      setObjectUrl(URL.createObjectURL(fileData));
    } else {
      setObjectUrl(undefined);
    }
  }, [fileData]);

  //切り取った画像のObjectUrlを作成し、ステイトに保存する
  const makeProfileImgObjectUrl = async () => {
    if (objectUrl) {
      const canvas = document.createElement("canvas");
      canvas.width = crop.width;
      canvas.height = crop.height;
      const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
      ctx.beginPath();
      ctx.arc(
        canvas.width / 2,
        canvas.height / 2,
        canvas.width / 2,
        0,
        2 * Math.PI,
        false
      );
      ctx.clip();

      const img = await loadImage(objectUrl);
      console.log(img.width, img.naturalWidth);
      ctx.drawImage(
        img,
        crop.x,
        crop.y,
        crop.width,
        crop.height,
        0,
        0,
        crop.width,
        crop.height
      );

      canvas.toBlob((result) => {
        if (result instanceof Blob) setProfileImg(URL.createObjectURL(result));
      });
    }
  };

  // canvasで画像を扱うため
  // アップロードした画像のObjectUrlをもとに、imgのHTMLElementを作る
  const loadImage = (src: string): Promise<HTMLImageElement> => {
    return new Promise((resolve) => {
      const img = new Image();
      img.src = src;
      img.onload = () => resolve(img);
    });
  };

  return (
    <div>
      <form
        onSubmit={(e: FormEvent<HTMLFormElement>) => {
          e.preventDefault();
          makeProfileImgObjectUrl();
        }}
      >
        <input
          type="file"
          onChange={(e: ChangeEvent<HTMLInputElement>) => {
            e.target.files && setFileData(e.target.files[0]);
          }}
        />
        <button>切り取り</button>
      </form>
      <div>
        {objectUrl && (
          <ReactCrop
            crop={crop}
            onChange={(c) => setCrop(c)}
            aspect={1}
            circularCrop={true}
            keepSelection={true}
          >
            <img src={objectUrl} alt="" style={{ width: "100%" }} />
          </ReactCrop>
        )}
      </div>
      <div>
        {profileImg ? <img src={profileImg} alt="プロフィール画像" /> : ""}
      </div>
    </div>
  );
};

export default CropImg;

Discussion

ログインするとコメントできます