今回は複数のcanvasタグを合成し、最終的に1つのcanvasに画像として結合する処理を取り上げたいと思います。 ↑こんな感じで画像を表示したcanvasと、キャプションを表示したcanvasを合成します。
Canvas合体
サンプル
以下のページから実際にサンプルを実行できます。 miku3.net
- 「=」ボタンをクリックすると左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