1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Canvas APIだけで画像をリサイズする最小実装【ライブラリ不要】

1
Posted at

きっかけ

画像リサイズのためだけに Sharp を入れるのが、なんとなく嫌だった。

Node.js 環境ならともかく、ブラウザで動かすだけなのにバンドルサイズを
増やしたくないという気持ちがあって、Canvas API だけで書いてみたら
意外とあっさり動いたので残しておきます。


コード全体

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>画像リサイズ</title>
</head>
<body>
  <input type="file" id="input" accept="image/*" />
  <br><br>
  <canvas id="canvas"></canvas>
  <br>
  <a id="download" download="resized.png">ダウンロード</a>

  <script>
    const input    = document.getElementById('input');
    const canvas   = document.getElementById('canvas');
    const download = document.getElementById('download');
    const ctx      = canvas.getContext('2d');

    const MAX_WIDTH  = 800;
    const MAX_HEIGHT = 800;

    input.addEventListener('change', (e) => {
      const file = e.target.files[0];
      if (!file) return;

      const img = new Image();
      img.onload = () => {
        const { width, height } = calcSize(img.width, img.height);

        canvas.width  = width;
        canvas.height = height;
        ctx.drawImage(img, 0, 0, width, height);

        download.href = canvas.toDataURL('image/png');
        download.textContent = `ダウンロード(${width}×${height})`;
      };
      img.src = URL.createObjectURL(file);
    });

    function calcSize(w, h) {
      if (w <= MAX_WIDTH && h <= MAX_HEIGHT) return { width: w, height: h };
      const ratio = Math.min(MAX_WIDTH / w, MAX_HEIGHT / h);
      return {
        width:  Math.round(w * ratio),
        height: Math.round(h * ratio),
      };
    }
  </script>
</body>
</html>

ファイルを選ぶと Canvas に描画されて、ダウンロードリンクが出てくるだけです。


ポイントを3つだけ

URL.createObjectURL を使う

img.src = URL.createObjectURL(file);

FileReader で Base64 に変換するコードをよく見かけますが、大きなファイルだと
変換だけで時間がかかります。createObjectURL はメモリ上のオブジェクトを
直接参照するので速いし、コードもシンプルです。

縦横比の維持は Math.min

const ratio = Math.min(MAX_WIDTH / w, MAX_HEIGHT / h);

縦と横それぞれのスケール比を出して、小さい方に合わせる。
これだけで縦長・横長どちらの画像でも意図した動きになります。

出力形式は toDataURL の引数で変えられる

canvas.toDataURL('image/jpeg', 0.85);  // JPEG、品質85%
canvas.toDataURL('image/png');          // PNG(デフォルト)

WebP は Chrome・Edge では動きますが Safari(特に古いバージョン)では
出力できないことがあるので、対応ブラウザを確認してから使う方が無難です。


使ってみて気になったこと

4000×3000px くらいの写真を何枚も連続で処理しようとすると、
ブラウザが重くなることがありました。1枚ずつ処理する分には特に問題ないですが、
まとめて処理したい場合は Web Worker に逃がした方が良さそうです。
そのあたりはまた試したら書きます。

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?