画像を 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>をそのまま渡せる。ローカルファイルはFileReaderでDataURLに変換してから。
実務でのコツ
- 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を使う。
