最近、canvasというHTMLの面白い仕様を知ったので、備忘録としてcanvasのチュートリアルをまとめたいと思います。本記事では、canvasに画像を表示させる 方法をまとめます。
コードはこちら:sample program
画像については、非商用に限りご自由に使ってください。
環境
- OS:Windows 11
- ブラウザ:Google Chrome (ver. 150.0.7871.187)
- エディタ:Visual Studio Code
- 使用言語:HTML / CSS / JavaScript
準備
HTML、CSS、JavaScriptのファイルとお好きな画像を用意してください。
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/nicolas-cusan/destyle.css@latest/destyle.css">
<link rel="stylesheet" href="css/style.css">
<title>canvas drawImage</title>
</head>
<body>
<canvas id="canvas" width="640" height="480"></canvas>
<script src="js/main.js"></script>
</body>
</html>
canvasは、width属性とheight属性を持ちます。
これらはcanvas内の描画領域を表す属性であり、初期値はwidth="300", height="150"です。
CSSに同様のプロパティがありますが、それらはページ上におけるcanvasの表示サイズとなります。そのため、canvasのwidth / height属性の値とCSS上でのwidth / heightが異なると、canvasの描画が引き延ばされたり歪んだりします。
CSS
#canvas {
border: 1px solid #000;
display: block;
height: 480px;
margin: 50px auto;
width: 640px;
}
CSSで簡単な装飾を加えます。
以上で準備は終了です。
画像の読み込みと表示方法
ここから、JavaScriptでcanvasに画像を表示させます。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 画像の読み込み
async function loadImage() {
const image = new Image();
image.src = './image/cat.jpg'; // 画像のパス(htmlファイルからの相対パス)
await image.decode();
return image;
}
// 画像の描画
async function drawImage() {
try {
const img = await loadImage();
const x = (canvas.width - img.width) / 2;
const y = (canvas.height - img.height) / 2;
ctx.drawImage(img, x, y);
} catch (error) {
console.log('表示に失敗しました', error);
}
}
drawImage();
上記のコードの説明を簡単にします。
まず、canvasに画像を描画するための準備として下のコードが必要となります。
const ctx = canvas.getContext('2d');
このコードは、canvas要素が持つメソッドの一つです。引数を2dに指定することで、2Dグラフィックを描画するためのメソッドやプロパティを使えるようになります。
次に、canvasに画像を描画する流れについてです。
1. 画像を取得・読み込み (image.src = './image/cat.jpg')
2. 画像を展開 (await image.decode();)
3. 画像を表示 (ctx.drawImage(img, x, y);)
まず、image.src = './image/cat.jpg' で描画する画像を取得します。
その後、image.decode(); で画像を展開します。
JPGやPNGなどの画像ファイルは、データ容量を小さくするためにデータを「圧縮」しています。ブラウザで画像を表示するためには、その圧縮されたデータをピクセル情報へと展開する「デコード」という処理が必要となります。
image.decode(); は、画像のデコードが完了するまで待機するためのメソッドです。
また、画像の読み込みと展開は、完了までに時間がかかる処理です。そのため、async / awaitを使い、画像の展開が完了してから次の処理へ進むようにしています。
複数の画像を表示
画像のパスを配列にまとめ、かつfor...of文を使うことで、複数の画像を表示させることもできます。
const canvas = document.getElementById('canvas02');
const ctx = canvas.getContext('2d');
const imageList = [
'./image/minicat-brown.jpg',
'./image/minicat-black.jpg'
];
// 画像の読み込み
async function loadImage(path) {
const image = new Image();
image.src = path; // 画像のパス(htmlファイルからの相対パス)
await image.decode();
return image;
}
// 画像の描画
async function drawImage() {
for (const path of imageList) {
try {
const img = await loadImage(path);
let index = 0;
const x = img.width * index;
const y = (canvas.height - img.height) / 2;
ctx.drawImage(img, x, y);
index += 1;
} catch (error) {
console.log('表示に失敗しました', error);
}
}
}
drawImage();
imageList に各画像のパスを保存します。
それらのパスをfor...of文で一つずつ取り出し、 loadImage の引数として渡しすことで画像を読み込んでいます。(この例では、2枚の画像が同じ横幅であることを前提に、画像の描画位置を計算しています。)
今回は、先ほど紹介した1枚の画像を描画するコードを大きく変更せず、複数の画像を扱う流れが分かりやすくなるように、このコードを紹介しました。
このコードでは、1枚の画像の読み込みと展開が完了してから、次の画像の処理を開始します。そのため、画像の枚数やファイルサイズが増えると、すべての画像を描画するまでに時間がかかる可能性があります。
例えば、画像1枚の読み込みに約1秒かかると仮定すると、2枚なら約2秒、3枚なら約3秒というように、処理時間が積み重なります。実際の時間は通信環境やブラウザのキャッシュなどによって変わりますが、画像を順番に読み込んでいる点は同じです。
複数の画像を効率よく扱う場合は、Promise.allSettled()などを使って画像を並行して読み込む方法があります。今回はcanvasに画像を描画する基本的な流れを中心に紹介するため、詳しい方法については別の記事で取り上げます。
最後に
今回はcanvasに画像を表示させる方法をまとめました。
久々の技術記事投稿でした。今回初めてgit hubを使ってソースコードを公開したのですが、少してこずりました。Gitにも慣れていく必要があります。今後もこうした技術記事を学習の一環として、また備忘録として投稿していこうと思います。
余談ですが、canvasは画像だけではなく、コードを書いて図形を描画することもできます。僕はまだcanvasに触れたばかりなので、今後別の機会に遊んでみようと思います。