EY-Office ブログ

ブラウザーのCanvseを使って画像処理

今回はネット上に多数ある情報かもしれませんが、HTML5で導入されたCanvasを使った簡単な画像処理です。

現在の仕事でReactクライアント(ブラウザー上)で選択した画像の大きさ等を調整してサーバーに送る必要があります。ブラウザー上で画像処理ができるnpm(JavaScript)ライブラリーもありますが、今回はCanvasを使い簡単な画像処理を行うコードを書いてみました。

Canvas Bing Image Creatorが生成した画像です

サンプルプログラム

今回のプログラムの画面は以下のようになります。

  1. 最初に、ファイル選択ボタンで画像ファイルを選択します
  2. その画像がファイル選択ボタンの下に表示されます
  3. 次に、Zoomボタンを押すと
  4. Zoomボタンの下に拡大縮小された画像が表示されます
    • ここでは幅が0.5倍、高さが2倍になっています

サンプルプログラムのコード

import React, { useState } from "react";

export default const App = () => {
  const [srcImage, setSrcImage] = useState<string|null>(null);       // ← ①
  const [zoomedImage, setZoomedImage] = useState<string|null>(null); // ← ②

  const loadImage = (e: React.ChangeEvent<HTMLInputElement>) => {    // ← ③
    const files = e.target.files;             // ← ④
    if (!files || !files[0]) return;

    const reader = new FileReader();
    reader.readAsDataURL(files[0]);           // ← ⑤
    reader.onload = () => {
      setSrcImage(reader.result as string);   // ← ⑥
    };
  }
  • ① 選択された元画像データが入るステート
    • 画像データはデータURL 形式の文字列です(base64エンコードされています)
  • ② 拡大縮小された画像データが入るステート
  • ③ 画像読み込み関数
  • <input type="file" />のイベントデータには選択されたファイルの情報が入っています
  • ⑤ ファイルの内容をFileReader.readAsDataURL()を使い読み込みます
  • ⑥ 読み込みが完了したら、画像データ(データURL形式)をステートに格納
  const zoomImage = async (xRate: number, yRate: number) => {  // ← ⑦
    if (!srcImage) return;
    const image = new Image();
    image.src = srcImage;
    await image.decode();                                 // ← ⑧

    const canvas = document.createElement('canvas');      // ← ⑨
    const ctx = canvas.getContext('2d');                  // ← ⑩
    if (!ctx) return;

    canvas.width  = image.width  * xRate;                 // ← ⑪
    canvas.height = image.height * yRate;
    ctx.scale(xRate, yRate);                              // ← ⑫
    ctx.drawImage(image, 0, 0);                           // ← ⑬

    const [, mimeType] = srcImage.match(/^data:(.*?);/) ??
                         ['', 'image/png'];               // ← ⑭
    setZoomedImage(canvas.toDataURL(mimeType));           // ← ⑮
  }
  • ⑦ 画像拡大縮小の関数
    • 引数 xRate:幅の拡大率、yRate:高さの拡大率
  • ⑧ 画像オブジェクトを作成し、元画像データを読み込みます
    • await image.decode()で画像読み込みの完了を待ちます
  • Canvasを作成
  • 2次元描画コンテキスト(画像処理の各種パラメーターの格納されたオブジェクト)を取得
  • ⑪ Canvasの幅・高さを拡大縮小後の画像サイズに設定
    • image.widthには元画像の幅です、image.heightは高さ
  • scale()で次で描画する画像のスケール(拡大率)を設定
  • ⑬ 元画像オブジェクトをCanvasに描画
    • ここで拡大縮小された画像がCanvasに書かれます
  • ⑭ 元画像のmime-typeを取得
    • データURLの先頭には data:image/png';のように画像の形式が書かれています
    • match()関数は正規表現にマッチしないとnullを戻すので、その場合はimage/pngを設定します
  • ⑮ Canvasの内容をデータURL形式に変換し、拡大縮小された画像をzoomedImageステートの格納します
  return (
    <div>
      <input type="file" onChange={loadImage} />                {#  ← ⑯ }
      <hr />                                                    {#  ↓ ⑰ }
      {srcImage && <img src={srcImage} alt="source image" style={{border: 'solid 1px blue'}} />}
      <hr />
      <button onClick={_ => zoomImage(0.5, 2.0)}>Zoom</button>  {#  ← ⑱ }
      <hr />                                                    {#  ↓ ⑲ }
      {zoomedImage && <img src={zoomedImage} alt="zoomed image" style={{border: 'solid 1px blue'}}/>}
    </div>
  );
}
  • ⑯ ファイル選択ボタン、選択されたらloadImage()関数が呼び出されます
  • ⑰ 選択された元(srcImage)画像の表示用imgタグ
  • ⑱ Zoomボタンを押すと、拡大縮小関数 zoomImage(0.5, 2.0) が呼び出されます
  • ⑲ 拡大縮小された(zoomedImage)画像の表示用imgタグ

まとめ

このようにCanvasを使った画像処理は、CanvasRenderingContext2Dが提供している関数で処理できる範囲で、かつ何らかの汎用的な画像処理ライブラリーを使った事のある方なら、難しくないと思います。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ