Canvas Todataurl() Returns Blank Image

HTML5 Canvas toDataURL returns blank

My intuition says that everything from var imageData = … should go into the img.onload function.

That means, at the relevant part the code becomes:

img.onload = function() {
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);

var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

document.body.appendChild(canvas); //picture gets uploaded

// Generate the image data

var Pic = canvas.toDataURL("image/png");

console.log(Pic); // => returns base64 value which when tested equivalent to blank
Pic = Pic.replace(/^data:image\/(png|jpg);base64,/, "")

// Sending image to Server
$.ajax({
// …
});
};
img.src = datauri;

The reason is that the line

ctx.drawImage(img, 0, 0, width, height);

correctly executes after the image has been loaded. But unfortunately, you don’t wait for loading when this line gets executed:

var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

and all subsequent lines.

The image needs to be loaded in order to draw it on the canvas. The canvas needs to contain the loaded image in order to call getImageData.

canvas + drawImage, toDataURL returns blank image

$(document).ready(function() {   var canvas = document.getElementById('canvas');   var context = canvas.getContext('2d');   var imageObj = new Image();
imageObj.onload = function() { context.drawImage(imageObj, 0, 0, 200, 200); var pngUrl = canvas.toDataURL(); console.log(pngUrl); $('#client_avatar').attr('src', pngUrl); };
imageObj.crossOrigin = 'anonymous'; //not necessary, if image hosted on same server imageObj.src = 'https://i.imgur.com/Q6aZlme.jpg';});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><canvas id="canvas" width="200" height="200"></canvas><img id="client_avatar">

Canvas toDataURL() returns blank image

Most likely there's some async event between the time you draw to the canvas and the time you call toDataURL. By default the canvas is cleared after every composite. Either prevent the canvas from being cleared by creating the WebGL context with preserveDrawingBuffer: true as in

var gl = canvas.getContext("webgl", {preserveDrawingBuffer: true});

or make sure toDataURL is called before exiting whatever event you're using to render. For example if you do this

function render() {
drawScene();
requestAnimationFrame(render);
}
render();

And somewhere else do this

someElement.addEventListener('click', function() {
var data = someCanvas.toDataURL();
}, false);

Those 2 events, the animation frame, and the click are not in sync and the canvas may be cleared between calling them. Note: The canvas won't appear cleared as it's double buffered but the buffer toDataURL and other commands that effect that buffer are looking at is cleared.

The solution is either use preserveDrawingBuffer or make your call to toDataURL inside the same event as rendering. For example

var captureFrame = false;

function render() {
drawScene();

if (captureFrame) {
captureFrame = false;
var data = someCanvas.toDataURL();
...
}

requestAnimationFrame(render);
}
render();

someElement.addEventListener('click', function() {
captureFrame = true;
}, false);

What's the point of preserveDrawingBuffer: false which is the default? It can be significantly faster, especially on mobile to not have to preserve the drawing buffer. Another way to look at it is the browser needs 2 copies of your canvas. The one you're drawing to and the one it's displaying. It has 2 ways to deal with these 2 buffers. (A) double buffer. Let you draw to one, display the other, swap the buffers when you're done rendering which is inferred from exiting any event that issued draw commands (B) Copy the contents of the buffer you're drawing to do the buffer that's being displayed. Swapping is much faster than copying. So, swapping is the default. It's up to the browser what actually happens. The only requirement is that if preserveDrawingBuffer is false that the drawing buffer get cleared after a composite (which is yet another async event and therefore unpredictable) if preserveDrawingBuffer is true then it must copy so that the drawingbuffer's contents is preserved.

Note that once a canvas has a context it will always have the same context. So in other words let's say you change the code that initializes the WebGL context but you still want to set preserveDrawingBuffer: true

There are at least 2 ways.

find the canvas first, get a context on it

since the code later will end up with the same context.

<script>
document.querySelector('#somecanvasid').getContext(
'webgl', {preserveDrawingBuffer: true});
</script>
<script src="script/that/will/use/somecanvasid.js"></script>

Because you've already created a context for that canvas whatever script comes after will get the same context.

augment getContext

<script>
HTMLCanvasElement.prototype.getContext = function(origFn) {
return function(type, attributes) {
if (type === 'webgl') {
attributes = Object.assign({}, attributes, {
preserveDrawingBuffer: true,
});
}
return origFn.call(this, type, attributes);
};
}(HTMLCanvasElement.prototype.getContext);
</script>
<script src="script/that/will/use/webgl.js"></script>

In this case any webgl context created after augmenting the getContext will have preserveDrawingBuffer set to true.

Blank image when executing canvas.toDataUrl()

So after much trawling through StackOverflow I found the function drawScene() which (somehow?) force toDataURL() to return the image data as required:

var canvas = $("#mandelbox-canvas")[0];
drawScene()
var imgData = canvas.toDataURL("image/png").split(',')[1];
console.log(imgData)

JavaScript canvas.toDataURL method returns blank image

You likely need to wait until after drawing the image before calling toDataURL. The fireball is only drawn after the image is loaded but the rotate (and everything after that) is called right after registering the onload event handler (but before it has actually loaded).

html5 canvas toDataURL returns blank image

You are first calling loadImages, then getting the data URL right after that loadImages call. However, as your loadImages implementation shows, calling it merely starts the loading process; the callback is called when all images have been loaded, which is some time in the future.

Currently you're getting a blank image because the images have not been loaded yet, so your callback is not called yet, and as a result your canvas is still empty.

In short, everything that relies on the images having being loaded (e.g. your expected toDataURL result) should be executed when those images are actually loaded - thus in the callback.

loadImages(sources, function(images) {
context.drawImage(images.darthVader, 250, 30, 250, 250);
context.drawImage(images.yoda, 530, 30, 250, 250);

var dataURL = canvas.toDataURL();
document.getElementById("canvasImg").src = dataURL;
});

Drawing multiple images on canvas. but toDataURL returns blank

First of all, yes, you're right in your guess. You are trying to get the image from canvas right after your loop, but at this moment no images are acually loaded. So your action is to wait for all images to load, and only then try to save the canvas. It could be achieved through promises for example:

var promises = [];
for(...your loop) {
...
var promise = new Promise(function(resolve, reject) {
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload=function(){
...draw image...
resolve(...resolve with your context...);
}
URL.revokeObjectURL(this.src);
});
img.src = URL.createObjectURL(array[i]);
promises.push(promise);
}

Promise.all(promises).then(function(...array of your contexts...) {
...make export here...
});

Second thing, is that the ability to get DataUrl depends from the sources of the images. If they are on the sepparate not your's domain, and they don't have cross-origin header with the * or your domains value, you will not be able to get the data, because the canvas becomes tainted according to Content Security Policy (Explaining what is tainted canvas). But anyway, if this scenario will happen, the Chrome and other modern browsers should warn about it in console.



Related Topics



Leave a reply



Submit