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.
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;
});
Canvas toDataURL() returns blank image in Firefox even with .onload and callbacks
There’s a bug open in Firefox about this: drawImage() fails silently when drawing an SVG image without @width or @height.
You can fix it by adding (go figure) width
and height
to the SVG:
<svg
xmlns="http://www.w3.org/2000/svg"
width="119" height="119" viewBox="0 0 119 119">
…
Updated Codepen
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)
html5 canvas toDataURL returns blank image
Have you seen this comment in the PHP docs for base64_decode
?
If you want to save data that is derived from a Javascript
canvas.toDataURL() function, you have to convert blanks into plusses.
If you do not do that, the decoded data is corrupted:<?php
$encodedData = str_replace(' ','+',$encodedData);
$decocedData = base64_decode($encodedData);
?>
Related Topics
How to Load Up CSS Files Using JavaScript
How to Detect If JavaScript Is Disabled
How to Clear the Canvas For Redrawing
Console.Log of Element.Children Shows 0 Length But Has Three Entries When Expanded Later
Jquery Set Cursor Position in Text Area
Why Does My Http://Localhost Cors Origin Not Work
How to Create a ≪Style≫ Tag With JavaScript
Using Jquery to Center a Div on the Screen
When Is a Cdata Section Necessary Within a Script Tag
Get Element from Within an Iframe
Html5 Form Required Attribute. Set Custom Validation Message
Selecting All Text in HTML Text Input When Clicked
Does Html5 Allow Drag-Drop Upload of Folders or a Folder Tree
How to Make a Browser to Browser (Peer to Peer) Connection
How to Set Value of Input Text Using Jquery