複数のcanvasを別のcanvas上でdrawImageを使って結合し、結合したcanvasのbase64を取得してサーバーに送信、png画像として保存したいのですが結合したcanvasのbase64がうまく取得できません。
base64っぽい文字列は取得できているのですが、保存した画像を見ると真っ白になっています。
let c = document.getElementById("canvas"); let ctx = c.getContext("2d"); let y = 0;//結合する画像のcanvasでの表示位置 //結合に使用するcanvasの大きさを設定 $("#canvas").attr({ height: ($("#img").height() * items.length) });//高さは保存した全ページを縦に表示できる高さにする $("#canvas").attr({ width: $("#img").width() }); _.forEach(items, (data) => {//itemsに結合したいcanvasのデータが格納されている let img = new Image(); img.src = data.imageData;//結合したいcanvasのbase64 img.onload = function () { ctx.drawImage(img, 0, y); y += $("#canvas").height(); } }); result = c.toDataURL("image/png");
色々試したところ、最後のbase64の取得を5秒待って実行されるようにすると正常に画像が保存できました。
setTimeout(function () { result = c.toDataURL("image/png"); },5000)
おそらくdrawImageでの描写が完全に終わる前に、base64を取得していたのが原因だと思われますが、
描写が完全に終わってからtoDataURLを実行する方法はあるのでしょうか?
上記のように5秒も待ってしまうと処理が遅くなりますし、待ち時間を短くすると画像の描写が完全に終わる前にbase64を取得してしまうことがあるかもしれないので、そのようなイベントやメソッドがあればいいのですが見つかりませんでした…
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
_ というのは Lodash(underscore.js?) を使っているのでしょうか?
https://lodash.com/docs/4.17.4#forEach
items が配列であれば第二引数にインデックスが来るので取得しておきます。
JavaScript
1_.forEach(items, (data, index) => {
すべての画像の load を待ってからでないと実行できないので、Promise(Deferred) を使います。
drawImage() の dy の計算を間違えていないか確認してください。
JavaScript
1const height = $("#img").height(); 2const promises = [];
jQuery
1 const deferred = new $.Deferred(); 2 promises.push(deferred.promise()); 3 let img = new Image(); 4 img.onload = function () { 5 ctx.drawImage(img, 0, height * index); 6 deferred.resolve(); 7 } 8 9 img.src = data.imageData;
jQuery
1$.when.apply($, promises).done(() => { 2 result = c.toDataURL("image/png"); 3});
https://api.jquery.com/category/deferred-object/
https://foreignkey.toyao.net/archives/1147
投稿2019/08/23 07:02
総合スコア13749
0
以下の記事は参考になりませんか?
canvas の画像をアップロード
http://surferonwww.info/BlogEngine/post/2015/07/02/upload-image-drawn-on-html5-canvas.aspx
不明な点があれば聞いてください。
【追記】
下の 2019/08/26 13:14 の私のコメントに「もう少し考えてみました・・・回答欄にそのあたりを追記しておきます」と書きましたが、それを以下に追記します。
コメント欄に「複数の画像を扱うということが何か関係してる」と書きましたがそんなことはなくて、ごく単純に一枚だけ扱った場合でも canvas.toDataURL("image/png"); を image.onload のリスナの外に記述すると Data Url は取得できません。
逆に言うと、一枚でも複数でも image.onload のリスナの中に canvas.toDataURL("image/png"); を書けば Data Url は取得できます。もちろん setTimeout で待つ必要は有りません。
複数の画像の場合、複数の image.onload イベントに複数のリスナがアタッチされますが、そのリスナにどのように canvas.toDataURL("image/png"); を記述するかが考えどころです。
画像の読み込みにかかる時間が画像のサイズなどによって異なるので、load イベント発生の順番は不定となりますが、それに対応するために最後の load イベントの発生を待って、そのイベントリスナで希望する順番に各 image を drawImage で canvas に書き込むのがよさそうです。
そうすれば、そのリスナで canvas に書き込んだ後、同じリスナ内で canvas.toDataURL("image/png"); によって Data Url を取得できます。
検証に使ったコードを以下にアップしておきます。元の画像は 226px x 150px の .png または .jpg 画像です。(ASP.NET のページを利用していますが、そこは本題とは関係ないので気にしないでください)
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="0076-Canvas.aspx.cs" Inherits="_0076_Canvas" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <script type="text/javascript"> window.onload = function () { var srcs = [ "/Images/image1.png", "/Images/image2.jpg", "/Images/image3.jpg", "/Images/image4.jpg", "/Images/image5.jpg" ]; var canvas = document.getElementById('mycanvas'); var context = canvas.getContext('2d'); // 単純に一枚の画像の場合 // これは OK だが、canvas.toDataURL("image/png") をリスナの外に出すとダメ //var image = new Image(); //image.src = "/Images/image1.png"; //image.onload = function () { // context.drawImage(image, 0, 0); // var result = canvas.toDataURL("image/png"); // document.getElementById("image1").src = result; //}; // 質問者さんのコードと似たような感じにしてみましたが、load イベントの発生タイミング // は i の順番にならず、canvas に描かれる順番が保証されないのでダメ //var y = 0; //for (let i = 0; i < srcs.length; i++) { // let image = new Image(); // image.src = srcs[i]; // image.onload = function () { // context.drawImage(image, 0, y); // y += 150; // } //} // canvas に描かれる順番を保証するには以下のようにするのがよさそう。そうすれば // リスナの中に canvas.toDataURL("image/png"); を記述でき Data Url が取得できる var images = []; var loadCount = 0; for (let i = 0; i < srcs.length; i++) { images[i] = new Image(); images[i].src = srcs[i]; images[i].onload = function () { loadCount++; if (loadCount == images.length) { for (let j = 0; j < images.length; j++) { context.drawImage(images[j], 0, j * 150); } // image.onload のリスナの中でないと Data Url が取得できない var result = canvas.toDataURL("image/png"); document.getElementById("image1").src = result; } }; } // ここではダメ。Data Url は取得できない //var result = canvas.toDataURL("image/png"); //document.getElementById("image1").src = result; } </script> </head> <body> <form id="form1" runat="server"> <div style="float: right;"> <canvas id="mycanvas" width="266" height="750"></canvas> </div> <div> <img id="image1" alt="" /> </div> </form> </body> </html>
結果は以下のようになります。
投稿2019/08/23 01:25
編集2019/08/26 05:10退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2019/08/23 02:11 編集
退会済みユーザー
2019/08/23 03:03
退会済みユーザー
2019/08/23 03:56 編集
退会済みユーザー
2019/08/23 04:01
退会済みユーザー
2019/08/23 05:15
退会済みユーザー
2019/08/24 05:17 編集
退会済みユーザー
2019/08/24 14:01
退会済みユーザー
2019/08/26 04:14
退会済みユーザー
2019/08/26 06:05
退会済みユーザー
2019/08/26 08:36
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2019/08/23 08:29
退会済みユーザー
2019/08/26 06:03