【JavaScript】Canvasを使って画像をリサイズする方法!

この記事では『Canvasを使って画像をリサイズする方法』について、以下の内容をサンプルコードを用いてわかりやすく解説します。

  • フロントエンドで画像リサイズを行う理由
  • Canvasとは
  • Canvasを使って画像をリサイズする方法
  • ファイル選択ダイアログで選択した画像をCanvasでリサイズする方法
  • リサイズした画像をダウンロードする方法

フロントエンドで画像リサイズを行う理由

スマートフォンやデジタルカメラの高解像度化に伴い、画像サイズが数MBに達することも多く、サーバーにアップロードする際の負荷が大きくなります。また、アップロードに時間がかかる可能性があります。

そこで、フロントエンド側で画像をリサイズすることで、以下のようなメリットが得られます。

  • レスポンスの高速化
    • 大きな画像はアップロードと表示に時間がかかりますが、リサイズしてから送信することで、レスポンスが速くなります。
  • サーバー負荷の軽減
    • クライアント側で画像をリサイズすることで、サーバー側での処理を軽減できます。
  • ユーザー体験の向上
    • アップロード時間が短縮され、ユーザーに快適な操作感を提供します。

この記事では、JavaScriptのCanvasを使って画像をリサイズする方法を説明します。

Canvasとは

まず、Canvasについて説明します。CanvasはWeb APIの一つで、JavaScriptを使って画像やグラフィックを動的に描画・操作するために使われています。

<canvas>要素自体は「空の描画領域」なのでそのままでは何も表示されませんが、JavaScriptを使用してCanvas APIを操作することで、画像を描画したり、編集したりすることができるようになります。

なお、<canvas>要素に描画する際には、2Dグラフィック用の「2Dコンテキスト」や3Dグラフィック用の「WebGLコンテキスト」を用いる必要があります。

この記事では、画像のリサイズに2Dコンテキスト(getContext('2d'))を用いています。

キャンバス API (Canvas API) は JavaScript と HTML の <canvas> 要素によってグラフィックを描く方法を提供します。他にも、アニメーション、ゲームのグラフィック、データの可視化、写真加工、リアルタイム動画処理などに使用することができます。

https://developer.mozilla.org/ja/docs/Web/API/Canvas_API

Canvasを使って画像をリサイズする方法

Canvasを使って、画像をリサイズしているシンプルなサンプルコードを示しています。以下のサンプルコードでは、同じディレクトリ内にある画像ファイル(sample.jpg)をリサイズして表示しています。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>画像リサイズ</title>
  </head>
  <body>
    <h3>元画像</h3>
    <!-- 同じディレクトリにある画像を表示 -->
    <img id="original-img" src="sample.jpg" alt="元画像" />
    <h3>リサイズした画像</h3>
    <img id="resized-img" src="" alt="リサイズした画像" />

    <script>
      // 「元画像を表示している<img>要素」と「リサイズした画像を表示する<img>要素」を取得
      const originalImg = document.getElementById('original-img');
      const resizedImg = document.getElementById('resized-img');

      // 元画像が読み込まれると実行される
      originalImg.onload = function () {
        // リサイズする幅を300pxに指定し、高さはアスペクト比を維持して調整
        const newWidth = 300;
        const aspectRatio = originalImg.height / originalImg.width;
        const newHeight = newWidth * aspectRatio;

        // Canvasの作成と描画
        const canvas = document.createElement('canvas'); // 新しい<canvas>要素を作成し、リサイズ用の描画エリアを設定
        canvas.width = newWidth;
        canvas.height = newHeight;
        const ctx = canvas.getContext('2d'); // Canvas に描画するために必要な CanvasRenderingContext2D を取得
        ctx.drawImage(originalImg, 0, 0, newWidth, newHeight); // Canvasを使って指定した幅と高さでリサイズして画像を描画

        // リサイズした画像を表示
        const resizedImgDataUrl = canvas.toDataURL('image/jpeg'); // リサイズした画像をData URL形式で返す
        resizedImg.src = resizedImgDataUrl;
      };
    </script>
  </body>
</html>

上記のサンプルコードについて説明します。

HTML

<h3>元画像</h3>
<!-- 同じディレクトリにある画像を表示 -->
<img id="original-img" src="sample.jpg" alt="元画像" />
<h3>リサイズした画像</h3>
<img id="resized-img" src="" alt="リサイズした画像" />

original-imgは元画像を表示するための<img>タグに設定している属性です。画像は同じディレクトリのsample.jpgを使用します。

resized-imgはリサイズした画像を表示するための<img>タグに設定している属性です。リサイズした画像をここに表示します。

JavaScript

HTML要素の取得

// 「元画像を表示している<img>要素」と「リサイズした画像を表示する<img>要素」を取得
const originalImg = document.getElementById('original-img');
const resizedImg = document.getElementById('resized-img');

「元画像を表示している<img>要素」と「リサイズした画像を表示する<img>要素」を取得しています。

元画像の読み込み

originalImg.onload = function () {
...
}

元画像を表示している要素(originalImg)の読み込みが完了すると、onloadイベントが発火します。

リサイズする幅と高さの指定

// リサイズする幅を300pxに指定し、高さはアスペクト比を維持して調整
const newWidth = 300;
const aspectRatio = originalImg.height / originalImg.width;
const newHeight = newWidth * aspectRatio;

リサイズする幅と高さを指定しています。高さはアスペクト比を維持して調整しています。

Canvasの作成と描画

// Canvasの作成と描画
const canvas = document.createElement('canvas'); // 新しい<canvas>要素を作成し、リサイズ用の描画エリアを設定
canvas.width = newWidth;
canvas.height = newHeight;
const ctx = canvas.getContext('2d'); // Canvas に描画するために必要な CanvasRenderingContext2D を取得
ctx.drawImage(originalImg, 0, 0, newWidth, newHeight); // Canvasを使って指定した幅と高さでリサイズして画像を描画

新しい<canvas>要素を作成し、リサイズ用の描画エリアを設定しています。また、リサイズした画像サイズに合わせて、Canvasのwidthheightを設定しています。

getContext('2d')を使用してCanvasRenderingContext2Dを取得しています。これにより、2Dの描画操作が可能になります。

drawImageメソッドを使用して、元画像をCanvasに描画しています。引数に新しい幅と高さを指定することで、指定したサイズにリサイズしています。

リサイズした画像を表示

// リサイズした画像を表示
const resizedImgDataUrl = canvas.toDataURL('image/jpeg'); // リサイズした画像をData URL形式で返す
resizedImg.src = resizedImgDataUrl;

canvas.toDataURL('image/jpeg')では、Canvasの内容をJPEG形式の「Data URL」に変換しています。このData URLは、画像を文字列の形式に変えたもので、Data URLをHTMLのsrc属性に使うことで画像を直接表示することができます。また、ダウンロードリンクにも使用することができます。このData URLをresizedImg.srcに設定することで、リサイズした画像がHTML上に表示されます。

ファイル選択ダイアログで選択した画像をCanvasでリサイズする方法

次に、ファイル選択ダイアログで選択した画像をCanvasを使ってリサイズしているサンプルコードを以下に示します。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>画像リサイズ</title>
  </head>
  <body>
    <!-- 画像選択:<input type="file">でファイルを選択すると、fileup関数が発火します。 -->
    <input type="file" accept="image/jpeg,image/png,image/gif" onchange="fileup(this)" />
    <h3>選択された画像</h3>
    <img id="original-img" src="" alt="元画像" />
    <h3>リサイズした画像</h3>
    <img id="resized-img" src="" alt="リサイズした画像" />

    <script>
      // 「元画像を表示する<img>要素」と「リサイズした画像を表示する<img>要素」を取得
      const originalImg = document.getElementById('original-img');
      const resizedImg = document.getElementById('resized-img');

      // fileup関数内の処理
      function fileup(input) {
        // 選択された画像ファイルを読み込むためのFileReaderオブジェクトを作成
        const reader = new FileReader();

        reader.onload = function () {
          // 画像処理用のHTMLImageElementオブジェクトの作成
          const imgReader = new Image();

          imgReader.onload = function () {
            // 選択された画像を表示
            originalImg.src = reader.result;

            // 画像タイプ取得
            const imgType = imgReader.src.substring(5, imgReader.src.indexOf(';'));

            // リサイズする幅を300pxに指定し、高さはアスペクト比を維持して調整
            const newWidth = 300;
            const aspectRatio = imgReader.height / imgReader.width;
            const newHeight = newWidth * aspectRatio;

            // Canvasの作成と描画
            const canvas = document.createElement('canvas'); // 新しい<canvas>要素を作成し、リサイズ用の描画エリアを設定
            canvas.width = newWidth;
            canvas.height = newHeight;
            const ctx = canvas.getContext('2d'); // Canvas に描画するために必要な CanvasRenderingContext2D を取得
            ctx.drawImage(imgReader, 0, 0, newWidth, newHeight); // Canvasを使って指定した幅と高さでリサイズして画像を描画

            // リサイズした画像を表示
            const resizedImgDataUrl = canvas.toDataURL(imgType); // リサイズした画像をData URL形式で返す
            resizedImg.src = resizedImgDataUrl;
          };
          // 画像ソースを指定し、画像を読み込む
          imgReader.src = reader.result;
        };
        // 選択されたファイルをData URL形式で読み込む
        reader.readAsDataURL(input.files[0]);
      }
    </script>
  </body>
</html>

上記のサンプルコードについて説明します。

HTML

<input type="file" accept="image/jpeg,image/png,image/gif" onchange="fileup(this)" />

<input type="file">要素を使用します。この要素をクリックすると、ファイル選択ダイアログが開きます。onchangeでファイル選択後にfileupイベントが発火します。

JavaScript

選択されたファイルをData URL形式で読み込む

function fileup(input) {
  // 選択された画像ファイルを読み込むためのFileReaderオブジェクトを作成
  const reader = new FileReader();
  reader.onload = function () {
    ...
  };
  reader.readAsDataURL(input.files[0]);
}

reader.readAsDataURL(input.files[0])によって、選択されたファイルをData URL形式で読み込んでいます。読み込みが完了すると、reader.onloadイベントが発火します。

読み込んだ画像をHTMLImageElementオブジェクトに変換

// 画像処理用のHTMLImageElementオブジェクトの作成
const imgReader = new Image();
imgReader.onload = () => {
 ...
};
// HTMLImageElementオブジェクトのsrcにFileReaderのresultプロパティを設定
imgReader.src = reader.result;

imgReader.src = reader.resultで、HTMLImageElementオブジェクトのsrcプロパティにFileReaderオブジェクトのresultプロパティを設定しています。この処理では、HTMLImageElementオブジェクトにFileReaderで読み込んだ画像データを設定しています。設定後、imgReader.onloadイベントが発火します。

【補足】リファクタリング

各処理を分割すると以下のようになります。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>画像リサイズ</title>
  </head>
  <body>
    <!-- 画像選択:<input type="file">でファイルを選択すると、fileup関数が発火します。 -->
    <input type="file" accept="image/jpeg,image/png,image/gif" onchange="fileup(this)" />
    <h3>選択された画像</h3>
    <img id="original-img" src="" alt="元画像" />
    <h3>リサイズした画像</h3>
    <img id="resized-img" src="" alt="リサイズした画像" />

    <script>
      // 「元画像を表示する<img>要素」と「リサイズした画像を表示する<img>要素」を取得
      const originalImg = document.getElementById('original-img');
      const resizedImg = document.getElementById('resized-img');

      // 画像ファイルをData URL形式で読み込み、Data URLを返す
      const readAsDataURL = function (imgFile) {
        return new Promise((resolve) => {
          // 選択された画像ファイルを読み込むためのFileReaderオブジェクトを作成
          const reader = new FileReader();
          reader.onload = function () {
            resolve(reader.result);
          };
          // 選択されたファイルをData URL形式で読み込む
          reader.readAsDataURL(imgFile);
        });
      };

      // 画像のURLを元にHTMLImageElementオブジェクトを作成し、読み込んだ画像を返す
      const loadImage = function (src) {
        return new Promise((resolve) => {
          // 画像処理用のHTMLImageElementオブジェクトの作成
          const imgReader = new Image();

          imgReader.onload = function () {
            resolve(imgReader);
          };

          // 画像ソースを指定し、画像を読み込む
          imgReader.src = src;
        });
      };

      // HTMLImageElementオブジェクトからリサイズした画像のData URLを生成して返す
      const resizeImgAndOutputDataUrl = function (img) {
        // MIMEタイプを取得
        const imgType = img.src.substring(5, img.src.indexOf(';'));

        // リサイズする幅を300pxに指定し、高さはアスペクト比を維持して調整
        const newWidth = 300;
        const aspectRatio = img.height / img.width;
        const newHeight = newWidth * aspectRatio;

        // Canvasの作成・Canvasへの描画:
        const canvas = document.createElement('canvas'); //Canvasの作成
        canvas.width = newWidth;
        canvas.height = newHeight;
        const ctx = canvas.getContext('2d'); // Canvas に描画するために必要な CanvasRenderingContext2D を取得
        ctx.drawImage(img, 0, 0, newWidth, newHeight); // Canvasを使って指定した幅と高さでリサイズして画像を描画
        return canvas.toDataURL(imgType); // リサイズした画像をData URL形式で返す
      };

      // fileup関数内の処理
      async function fileup(input) {
        // 選択された画像ファイルをData URL形式で取得
        const inputImgDataUrl = await readAsDataURL(input.files[0]);

        // 元画像を表示
        originalImg.src = inputImgDataUrl;

        // Data URLからHTMLImageElementオブジェクトを作成
        const inputImg = await loadImage(inputImgDataUrl);

        // HTMLImageElementオブジェクトをリサイズしてData URL形式で取得
        const resizedImgDataUrl = resizeImgAndOutputDataUrl(inputImg);

        // リサイズした画像を表示
        resizedImg.src = resizedImgDataUrl;
      }
    </script>
  </body>
</html>

リサイズした画像をダウンロードする方法

以下のサンプルコードでは、ファイル選択ダイアログで開いた画像をCanvasを使ってリサイズし、そのリサイズ画像をダウンロードできるようにしています。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>画像リサイズ</title>
  </head>
  <body>
    <!-- 画像選択:<input type="file">でファイルを選択すると、fileup関数が発火します。 -->
    <input type="file" accept="image/jpeg,image/png,image/gif" onchange="fileup(this)" />
    <h3>選択された画像</h3>
    <img id="original-img" src="" alt="元画像" />
    <h3>リサイズした画像</h3>
    <img id="resized-img" src="" alt="リサイズした画像" />
    <p><a id="download" href="#" download="no-name.png">ダウンロード</a></p>

    <script>
      // 「元画像を表示する<img>要素」と「リサイズした画像を表示する<img>要素」を取得
      const originalImg = document.getElementById('original-img');
      const resizedImg = document.getElementById('resized-img');

      // fileup関数内の処理
      function fileup(input) {
        // 選択された画像ファイルを読み込むためのFileReaderオブジェクトを作成
        const reader = new FileReader();

        reader.onload = function () {
          // 画像処理用のHTMLImageElementオブジェクトの作成
          const imgReader = new Image();

          imgReader.onload = function () {
            // 選択された画像を表示
            originalImg.src = reader.result;

            // 画像タイプ取得
            const imgType = imgReader.src.substring(5, imgReader.src.indexOf(';'));

            // リサイズする幅を300pxに指定し、高さはアスペクト比を維持して調整
            const newWidth = 300;
            const aspectRatio = imgReader.height / imgReader.width;
            const newHeight = newWidth * aspectRatio;

            // Canvasの作成と描画
            const canvas = document.createElement('canvas'); // 新しい<canvas>要素を作成し、リサイズ用の描画エリアを設定
            canvas.width = newWidth;
            canvas.height = newHeight;
            const ctx = canvas.getContext('2d'); // Canvas に描画するために必要な CanvasRenderingContext2D を取得
            ctx.drawImage(imgReader, 0, 0, newWidth, newHeight); // Canvasを使って指定した幅と高さでリサイズして画像を描画

            // リサイズした画像を表示
            const resizedImgDataUrl = canvas.toDataURL(imgType); // リサイズした画像をData URL形式で返す
            resizedImg.src = resizedImgDataUrl;

            // ダウンロード設定:リサイズした画像をダウンロードできるようにします。
            const dl = document.getElementById('download');
            dl.href = resizedImgDataUrl;
            // 元のファイル名を取得
            const originalFile = input.files[0];
            const originalFileName = originalFile.name;
            // ダウンロードのファイル名を変更
            dl.download = `resized_${originalFileName}`;
          };
          // 画像ソースを指定し、画像を読み込む
          imgReader.src = reader.result;
        };
        // 選択されたファイルをData URL形式で読み込む
        reader.readAsDataURL(input.files[0]);
      }
    </script>
  </body>
</html>

HTML

<a id="download" href="#" download="no-name.png">ダウンロード</a>

<a>タグはリンクとして機能しますが、download属性を設定することで、リンク先のファイルをダウンロードできるようにしています。この属性にはダウンロードファイルのファイル名も指定でき、ここでは初期設定として"no-name.png"としています。クリックするとリサイズした画像がダウンロードされます。

JavaScript

ダウンロード設定

// ダウンロード設定:リサイズした画像をダウンロードできるようにします。
const dl = document.getElementById('download');
dl.href = resizedImgDataUrl;
// 元のファイル名を取得
const originalFile = input.files[0];
const originalFileName = originalFile.name;
// ダウンロードのファイル名を変更
dl.download = `resized_${originalFileName}`;

dl.href = resizedImgDataUrlでは、リサイズした画像のData URLをリンクのhref属性に設定しています。これにより、リンクをクリックしたときにリサイズ画像がダウンロードされるようになります。

また、selectedFileNameには、選択された元ファイルの名前が格納されているため、この名前に基づいてdownload属性をresized_${selectedFileName}に更新しています。これにより、ダウンロードしたファイルの名前は元のファイル名にresized_を加えた名前になります。

本記事のまとめ

この記事では『Canvasを使って画像をリサイズする方法』について、以下の内容を説明しました。

  • フロントエンドで画像リサイズを行う理由
    • サーバー負荷の軽減、レスポンスの高速化、ユーザー体験の向上。
  • Canvasとは
    • HTMLの<canvas>要素とJavaScriptのCanvas APIを使い、動的な画像描画や操作が可能。
  • Canvasを使って画像をリサイズする方法
    • JavaScriptで<canvas>要素を作成し、指定した幅と高さで画像を描画。
    • drawImage()を用いてアスペクト比を保持したリサイズ。
  • ファイル選択ダイアログを利用した画像リサイズ
    • <input type="file">を利用し、選択画像をCanvasでリサイズ。
  • リサイズした画像をダウンロードする方法
    • toDataURL()でData URLを取得し、リンクのhref属性に設定してダウンロード可能に。
  • サンプルコードのリファクタリング
    • 各処理を関数化し、可読性を向上。

お読み頂きありがとうございました。