canvas内で画像をトリミングして配置する

canvas内で画像を読み込み、それを任意の形にトリミングしてからcanvas内に配置する。

See the Pen Untitled by nekuta (@nekuta) on CodePen.

角丸の四角を作る

処理はまるっとこちらのブログの方を参考にさせていただきました。

/**
* 角丸の四角を作り出す
* @param context
* @param x
* @param y
* @param width
* @param height
* @param radius
*/
const createRoundRect = (context: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number) => {
  // 左上移動
  context.moveTo(x + radius, y);
  // 右上に繋がる線
  context.lineTo(x + width - radius, y);
  // 弧
  context.arcTo(x + width, y, x + width, y + radius, radius);
  // 右下に繋がる線
  context.lineTo(x + width, y + height - radius);
  // 弧
  context.arcTo(x + width, y + height, x + width - radius
    , y + height, radius);
  // 左下に繋がる線
  context.lineTo(x + radius, y + height);
  // 弧
  context.arcTo(x, y + height, x, y + height - radius, radius);
  // 右上に繋がる線
  context.lineTo(x, y + radius);
  // 弧
  context.arcTo(x, y, x + radius, y, radius);
  context.closePath();
}
//呼び出し
createRoundRect(newCanvasContext, 0, 0, PHOTO_BASE_WIDTH, PHOTO_BASE_HEIGHT, 24);

トリミング用のcanvasを作る

そのままだと画像をトリミングできないので、新しくトリミング用のcanvasを作ります。

  //切り抜き用のcanvas作成
  const newCanvas = document.createElement("canvas");
  newCanvas.width = PHOTO_BASE_WIDTH;
  newCanvas.height = PHOTO_BASE_HEIGHT;

  const newCanvasContext = newCanvas.getContext("2d");


  //画像を読ませる(今回はbase64のものを読み込む)
  const photo_img = new Image();
  photo_img.src = `data:image/jpeg;base64,${imgUrl}`;

//画像が読まれたら
photo_img.onload = () => {
    //HTMLImageElement.naturalWidth等で、画像本来の幅を取得
    let naturalWidth = photo_img.naturalWidth;
    let naturalHeight = photo_img.naturalHeight;

    //canvasのアスペクト比
    let canvasAspect = PHOTO_BASE_WIDTH / PHOTO_BASE_HEIGHT;
    //画像のアスペクト比
    let imgAspect = naturalWidth / naturalHeight;

    // (寸法だけを知りたいので)破棄する
    URL.revokeObjectURL(photo_img.src);

    let imgleft;
    let imgwidth;
    let imgheight;
    let imgtop;

    //一度切り抜く必要があるためphotoを別のcanvasに描画
    if (newCanvasContext) {

      newCanvasContext.fillStyle = "rgb(150, 150, 150)";

      if (type === "roundRect") {
        //角丸の四角
        createRoundRect(newCanvasContext, 0, 0, PHOTO_BASE_WIDTH, PHOTO_BASE_HEIGHT, 24);
      } else if (type === "circle") {
        //丸
        newCanvasContext.beginPath();
        newCanvasContext.arc(PHOTO_BASE_WIDTH / 2, PHOTO_BASE_WIDTH / 2, PHOTO_BASE_WIDTH / 2, 0 * Math.PI / 180, 360 * Math.PI / 180, false);

      } else {
        //普通の四角
        newCanvasContext.rect(0, 0, PHOTO_BASE_WIDTH, PHOTO_BASE_HEIGHT);
      }

      newCanvasContext.fill();

      // 切り抜き
      newCanvasContext.clip();

      if (photoMode === "100%" && type != "circle") {
        if (imgAspect >= canvasAspect) {
          // 画像が横長
          imgleft = 0;
          imgwidth = PHOTO_BASE_WIDTH;
          imgheight = PHOTO_BASE_WIDTH / imgAspect;
          imgtop = (PHOTO_BASE_HEIGHT - imgheight) / 2;
        } else {
          // 画像が縦長
          imgtop = 0;
          imgheight = PHOTO_BASE_HEIGHT;
          imgwidth = PHOTO_BASE_HEIGHT * imgAspect;
          imgleft = (PHOTO_BASE_WIDTH - imgwidth) / 2;
        }

      } else {
        imgleft = (PHOTO_BASE_WIDTH - naturalWidth) / 2;
        imgwidth = naturalWidth;
        imgheight = naturalHeight;
        imgtop = (PHOTO_BASE_HEIGHT - naturalHeight) / 2;
      }

      //画像を描画
      newCanvasContext.drawImage(
        photo_img,
        0,
        0,
        naturalWidth,
        naturalHeight,
        imgleft,
        imgtop,
        imgwidth,
        imgheight
      );
    }

    //canvasへnewCanvasを描画
    context.drawImage(
      newCanvas,
      0,
      0,
      PHOTO_BASE_WIDTH,
      PHOTO_BASE_HEIGHT,
      photoData.x,
      photoData.y,
      PHOTO_BASE_WIDTH,
      PHOTO_BASE_HEIGHT
    );
  }

参考サイト

HTML5のcanvas要素 – 複雑な図形を描く (円、角丸四角、N角星、N葉っぱ花など) – liguofeng29’s blog

HTMLImageElement.naturalWidth – 画像の本来の横幅 | HTML Element

【問題】アスペクト比を維持して画像を表示【JavaScript,Canvas】 #JavaScript – Qiita

clip()-Canvasリファレンス