JavaScript 逆引き集 | 画像を Canvas に描画

JavaScript JavaScript
スポンサーリンク

画像を Canvas に描画 — ctx.drawImage(img, 0, 0)

Canvas に画像を描くときの基本は「画像が読み込まれてから drawImage を呼ぶ」こと。img.onload の中で描画すれば失敗しません。drawImage には用途別に3つの呼び方があります。


基本の使い方(画像をそのまま描く)

<canvas id="c" width="300" height="200" style="border:1px solid #ccc"></canvas>
<script>
  const canvas = document.getElementById("c");
  const ctx = canvas.getContext("2d");

  const img = new Image();
  img.src = "photo.jpg"; // 画像ファイルのパス

  img.onload = () => {
    // 左上(0,0)に原寸で描画
    ctx.drawImage(img, 0, 0);
  };
</script>
HTML
  • ポイント: 画像は非同期で読み込まれるため、img.onload の中で drawImage を呼ぶ。

3つのオーバーロード(使い分け早見)

  • 原寸で描画:drawImage(image, dx, dy)
    • 例: ctx.drawImage(img, 0, 0)
  • リサイズして描画:drawImage(image, dx, dy, dw, dh)
    • 例: ctx.drawImage(img, 0, 0, 150, 100)(幅150・高さ100に縮小)
  • トリミング+リサイズ:drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
    • 例: スプライトの一部を切り出して指定サイズで描画

例題1: 画像を縮小してキャンバスに収める

<canvas id="c1" width="300" height="200" style="border:1px solid #ccc"></canvas>
<script>
  const ctx = document.getElementById("c1").getContext("2d");
  const img = new Image();
  img.src = "large.jpg";

  img.onload = () => {
    // キャンバスに収まるように比率を保って縮小
    const scale = Math.min(300 / img.width, 200 / img.height);
    const w = img.width * scale;
    const h = img.height * scale;
    const x = (300 - w) / 2;
    const y = (200 - h) / 2;

    ctx.imageSmoothingQuality = "high"; // 縮小の綺麗さ
    ctx.drawImage(img, x, y, w, h);
  };
</script>
HTML
  • ポイント: 比率(アスペクト比)を保つには「短辺基準のスケール」を使う。

例題2: スプライトから一部を切り出して描画(トリミング)

<canvas id="c2" width="200" height="200" style="border:1px solid #ccc"></canvas>
<script>
  const ctx = document.getElementById("c2").getContext("2d");
  const img = new Image();
  img.src = "sprite.png"; // 横にキャラが並んだ画像など

  img.onload = () => {
    // 1つ目のコマを切り出して中央に等倍描画
    const sx = 0, sy = 0, sw = 64, sh = 64; // ソース内の切り出し範囲
    const dx = 68, dy = 68, dw = 64, dh = 64; // キャンバス描画位置とサイズ
    ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
  };
</script>
HTML
  • ラベル: s* はソース画像内の矩形、d* はキャンバス上の矩形。

例題3: <img> 要素やファイル選択から描画

<img id="source" src="photo.jpg" style="max-width:120px">
<canvas id="c3" width="300" height="200" style="border:1px solid #ccc"></canvas>

<script>
  const imgEl = document.getElementById("source");
  const ctx = document.getElementById("c3").getContext("2d");

  imgEl.onload = () => {
    ctx.drawImage(imgEl, 0, 0, 300, 200); // <img>要素をそのままソースに使える
  };
</script>
HTML
<input type="file" id="file" accept="image/*">
<canvas id="c4" width="300" height="200" style="border:1px solid #ccc"></canvas>

<script>
  const input = document.getElementById("file");
  const ctx = document.getElementById("c4").getContext("2d");

  input.addEventListener("change", () => {
    const f = input.files[0];
    if (!f) return;

    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => ctx.drawImage(img, 0, 0, 300, 200);
      img.src = reader.result; // DataURL経由で表示
    };
    reader.readAsDataURL(f);
  });
</script>
HTML
  • ポイント: <img> をそのまま渡せる。ローカルファイルは FileReaderDataURL に変換してから。

実務でのコツ

  • Canvasのサイズは属性で指定: <canvas width="W" height="H"> を必ず付ける。CSSだけだと内部解像度が合わなくなる。
  • 画像が巨大なとき: 一度小さめのオフスクリーンCanvasに縮小してから本番Canvasに描くと綺麗に速くなる。
  • 画像補間の品質: 縮小時の品質は ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; を設定。
  • 非同期読み込みの徹底: 常に onload 待ち。img.complete を見ても、失敗時など不十分なのでイベントで描く。
  • クロスオリジン画像: 別ドメイン画像を加工して toDataURL などで取り出すときは、サーバー側の CORS 設定と img.crossOrigin = 'anonymous' が必要。

ありがちなハマりポイントと対策

  • 何も描かれない:
    • 画像読み込み前に描いている。img.onload の中で描画する。
    • Canvasの width/height が未設定。属性で指定する。
  • 引き伸ばしでぼやける:
    • 大幅な拡大縮小は品質が落ちる。段階的に縮小、imageSmoothingQuality='high' を使う。
  • 切り出し範囲が合わない:
    • sx, sy, sw, sh が画像内の矩形、dx, dy, dw, dh がキャンバス上。入れ替えない。

練習問題(サムネイル生成)

「任意の画像を選んで、最大幅200pxのサムネイルとして中央に描画」

<input type="file" id="pick" accept="image/*">
<canvas id="thumb" width="220" height="220" style="border:1px solid #ccc"></canvas>

<script>
  const input = document.getElementById("pick");
  const ctx = document.getElementById("thumb").getContext("2d");

  input.addEventListener("change", () => {
    const f = input.files[0];
    if (!f) return;

    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => {
        const maxW = 200;
        const scale = Math.min(maxW / img.width, maxW / img.height, 1);
        const w = img.width * scale;
        const h = img.height * scale;
        const x = (220 - w) / 2;
        const y = (220 - h) / 2;

        ctx.clearRect(0, 0, 220, 220);
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = "high";
        ctx.drawImage(img, x, y, w, h);
      };
      img.src = reader.result;
    };
    reader.readAsDataURL(f);
  });
</script>
HTML

直感的な指針

  • 画像は「読み込んでから描く」が合言葉。
  • 原寸・リサイズ・トリミングの3パターンを覚えると大体の用途に対応できる。
  • キャンバスサイズは必ず属性で設定し、品質が欲しいときは imageSmoothingQuality を使う。
タイトルとURLをコピーしました