今回は複数のcanvasタグを合成し、最終的に1つのcanvasに画像として結合する処理を取り上げたいと思います。
↑こんな感じで画像を表示したcanvasと、キャプションを表示したcanvasを合成します。
Canvas合体
サンプル
以下のページから実際にサンプルを実行できます。
- 「=」ボタンをクリックすると左2つのCanvasを合成し、一番右側のcanvasに合成結果を出力します。
- 消しゴムボタンをクリックすると合成結果を削除します。
ソースコード
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CANVAS合成</title>
<link rel="stylesheet" href="style.css" type="text/css" media="all">
</head>
<body>
<h1>CANVAS合成</h1>
<section id="contents">
<div class="flex-container">
<!-- 合成するcanvasその1 -->
<canvas id="image1" width="200" height="170"></canvas>
<div><img src="icon/plus-solid.svg" width="32" height="32"></div>
<!-- 合成するcanvasその2 -->
<canvas id="image2" width="200" height="170"></canvas>
<p><button type="button" id="btn-concat"><img src="icon/equals-solid.svg" width="32" height="32"></button></p>
<!-- 合成結果用のcanvas -->
<canvas id="concat" width="200" height="170"></canvas>
</div>
<!-- 消しゴム -->
<p id="eraser"><button type="button" id="btn-eraser"><img src="icon/eraser-solid.svg" width="32" height="32"></button></p>
</section>
<ul>
<li>「=」ボタンをクリックすると2つのcanvasを合成します</li>
<li>「<img src="icon/eraser-solid.svg" width="18" height="18">」ボタンをクリックすると合成結果をクリアします</li>
</ul>
<script>
window.onload = () => {
// #image1に画像を描画
drawImage1();
// #image2にテキストを描画
drawImage2();
// 「+」ボタンを押したら合成
document.querySelector("#btn-concat").addEventListener("click", ()=>{
concatCanvas("#concat", ["#image1", "#image2"]);
});
// 「消しゴム」ボタンを押したらクリア
document.querySelector("#btn-eraser").addEventListener("click", ()=>{
eraseCanvas("#concat");
});
};
/**
* [onload] うな重の画像を描画
*/
function drawImage1(){
const Unaju = new Image();
Unaju.src = "image/unajyu.png";
Unaju.onload = () =>{
const canvas = document.querySelector("#image1");
const ctx = canvas.getContext("2d");
ctx.drawImage(Unaju, 0, 0, canvas.width, canvas.height);
}
}
/**
* [onload] テキスト「うな重」を描画
*/
function drawImage2(){
const canvas = document.querySelector("#image2");
const ctx = canvas.getContext("2d");
ctx.font = "32px serif";
ctx.fillStyle = "Red";
ctx.fillText("うな重", 45, 150);
}
/**
* Canvas合成
*
* @param {string} base 合成結果を描画するcanvas(id)
* @param {array} asset 合成する素材canvas(id)
* @return {void}
*/
async function concatCanvas(base, asset){
const canvas = document.querySelector(base);
const ctx = canvas.getContext("2d");
for(let i=0; i<asset.length; i++){
const image1 = await getImagefromCanvas(asset[i]);
ctx.drawImage(image1, 0, 0, canvas.width, canvas.height);
}
}
/**
* Canvasをすべて削除する
*
* @param {string} target 対象canvasのid
* @return {void}
*/
function eraseCanvas(target){
const canvas = document.querySelector(target);
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
/**
* Canvasを画像として取得
*
* @param {string} id 対象canvasのid
* @return {object}
*/
function getImagefromCanvas(id){
return new Promise((resolve, reject) => {
const image = new Image();
const ctx = document.querySelector(id).getContext("2d");
image.onload = () => resolve(image);
image.onerror = (e) => reject(e);
image.src = ctx.canvas.toDataURL();
});
}
</script>
</body>
</html>
解説
大雑把なロジック
考え方は非常にシンプルで、合体させたいcanvasを画像に変換し、最終結果を出力するcanvasにdrawImage()
で貼り付けているだけです。
canvasを画像に変換
getImagefromCanvas()
関数で指定したcanvasを画像に変換しています。
function getImagefromCanvas(id){
return new Promise((resolve, reject) => {
const image = new Image();
const ctx = document.querySelector(id).getContext("2d");
image.onload = () => resolve(image);
image.onerror = (e) => reject(e);
image.src = ctx.canvas.toDataURL();
});
}
ポイントはPromiseを利用することです。image.src
でcanvasのデータを画像として登録する際に非同期処理となるため、うまく後続処理が行われません。同様にgetImagefromCanvas()
を利用しているconcatCanvas()
では関数を定義する際にasync
を付加し、getImagefromCanvas()
を呼ぶ前にawait
をつけています。
async function concatCanvas(base, asset){
//.....
for(let i=0; i<asset.length; i++){
const image1 = await getImagefromCanvas(asset[i]);
//.....
}
}
素材
参考ページ
- https://developer.mozilla.org/ja/docs/Drawing_text_using_a_canvas
- https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors
- https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Canvas_tutorial/Using_images
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises
このブログを応援する
お寄せいただいたお気持ちは全額サーバ代や次の記事を執筆するための原資として活用させていただいております。この記事が参考になった場合などぜひご検討ください。