I wrote a few lines of code to draw an image but seems like it keeps on loading, I tried some W3 School code using the basic implementation but that works fine.
What is happening here is it looks like its stuck in some invisible loop and unable to draw an image.
<script>
var canvas=null;
var context=null;
setup=function(){
canvas=document.getElementById("my_canvas");
context=canvas.getContext('2d');
canvas.width=1200;
canvas.height=720;
img=new Image();
img.onload=onImageLoad;
img.src="http://imgge.bg.ac.rs/images/logo1.jpg";
context.drawImage(img,192,192);
};
onImageLoad=function(){
document.write("done !!");
context.drawImage(img,155,10);
};
setup();
</script>
I replaced your document.write() with a console.log() and it works fine:
var canvas = null;
var context = null;
var setup = function() {
canvas = document.getElementById("my_canvas");
context = canvas.getContext('2d');
canvas.width = 1200;
canvas.height = 720;
img = new Image();
img.onload = onImageLoad;
img.src = "http://imgge.bg.ac.rs/images/logo1.jpg";
context.drawImage(img, 192, 192);
};
onImageLoad = function() {
console.log("done !!");
context.drawImage(img, 155, 10);
};
setup();
<canvas id="my_canvas"></canvas>
For the reason why document.write() causes the canvas to not be displayed, read document.write() overwriting the document?
You might notice a difference between the first and consecutive runs due to img being cached by your browser. A cached image is immediately available for drawing within the setup() function.
First you don't need to write context.drawImage(img,192,192); two times only write inside the image.onload() function. when you write anything using using document.write("Done !"); , it will write the content on this document(HTML) page and may create problems. Instead of that write message on <p> tag instead of on whole document.
here is a snippet for your code:
var canvas=null;
var context=null;
setup=function(){
canvas=document.getElementById("my_canvas");
context=canvas.getContext('2d');
canvas.width=1200;
canvas.height=720;
img=new Image();
img.onload=onImageLoad;
img.src="http://imgge.bg.ac.rs/images/logo1.jpg";
//context.drawImage(img,192,192);
};
onImageLoad=function(){
context.drawImage(img,155,10);
document.getElementById("report").innerHTML="Done !";
};
setup();
<canvas id="my_canvas"></canvas>
<p id="report"></p>
Related
I'm trying to get a base64 version of a canvas in HTML5.
Currently, the base64 image that I get from the canvas is blank.
I have found similar questions for example this one:
HTML Canvas image to Base64 problem
However, the issue that I have is that because I am adding 2 images in the canvas, I cannot use the example provided in those answers as most of them are using single image.
I understand that I have to wait until the canvas image is loaded properly before trying to get the base64 image. But I don't know how in my case.
This is my code:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
var imageObj1 = new Image();
imageObj1.src = "http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg";
imageObj1.onload = function() {
context.drawImage(imageObj1, 0, 180, canvas.width, canvas.height);
};
var imageObj2 = new Image();
imageObj2.src = "http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"
imageObj2.onload = function() {
context.drawImage(imageObj2, 0, 0, canvas.width, 180);
};
// get png data url
//var pngUrl = canvas.toDataURL();
var pngUrl = canvas.toDataURL('image/png');
// get jpeg data url
var jpegUrl = canvas.toDataURL('image/jpeg');
$('#base').val(pngUrl);
<div class="contents" style="overflow-x:hidden; overflow-y:scroll;">
<div style="width:100%; height:90%;">
<canvas id="myCanvas" class="snap" style="width:100%; height:100%;" onclick="takephoto()"></canvas>
</div>
</div>
<p>
This is the base64 image
</p>
<textarea id="base">
</textarea>
and this is a working FIDDLE:
https://jsfiddle.net/3p3e6Ldu/1/
Can someone please advice on this issue?
Thanks in advance.
EDIT:
As suggested in the comments bellow, i tried to use a counter and when the counter reaches a specific number, I convert the canvas to base64.
Like so:https://jsfiddle.net/3p3e6Ldu/4/
In both your examples (the one from the question and the one from the comments), the order of commands does not really respect the async nature of the task at hand.
In your later example, you should put the if( count == 2 ) block inside the onload callbacks to make it work.
However, even then you will run into the next problem: You are loading the images from different domains. You can still draw them (either into the canvas or using an <img> tag), but you are not able to access their contents directly. Not even with the detour of using the <canvas> element.
I changed to code so it would work, if the images are hosted on the same domain. I also used a function to load the image and promises to handle the callbacks. The direct way of using callbacks and a counting variable, seem error-prone to me. If you check out the respective fiddle, you will notice the SecurityError shown. This is the result of the aforementioned problem with the Same-Origin-Policy I mentioned.
A previous question of mine in a similar direction was about how to detect, if I can still read the contents of a <canvas> after adding some images.
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
// function to retrieve an image
function loadImage(url) {
return new Promise((fulfill, reject) => {
let imageObj = new Image();
imageObj.onload = () => fulfill(imageObj);
imageObj.src = url;
});
}
// get images
Promise.all([
loadImage("http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg"),
loadImage("http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"),
])
.then((images) => {
// draw images to canvas
context.drawImage(images[0], 0, 180, canvas.width, canvas.height);
context.drawImage(images[1], 0, 0, canvas.width, 180);
// export to png/jpg
const pngUrl = canvas.toDataURL('image/png');
const jpegUrl = canvas.toDataURL('image/jpeg');
// show in textarea
$('#base').val(pngUrl);
})
.catch( (e) => alert(e) );
I am working with a single canvas that allows the user to click on a window pane in a window image. The idea is to show where the user has clicked. The image will then be modified (by drawing a grill on the window) and then saved to in JPEG. I am saving the canvas image prior to the click function because I don't want the selection box to show in the final image. However, Firefox often displays a blank canvas when restoring the canvas where IE and Chrome do not. This works perfectly in Chrome and IE. Any suggestions? Does Firefox have a problem with toDataURL()? Maybe some async issue going on here? I am also aware that saving a canvas in this fashion is memory intensive and there may be a better way to do this but I'm working with what I have.
Code:
/**
* Restores canvas from drawingView.canvasRestorePoint if there are any restores saved
*/
restoreCanvas:function()
{
var inverseScale = (1/drawingView.scaleFactor);
var canvas = document.getElementById("drawPop.canvasOne");
var c = canvas.getContext("2d");
if (drawingView.canvasRestorePoint[0]!=null)
{
c.clearRect(0,0,canvas.width,canvas.height);
var img = new Image();
img.src = drawingView.canvasRestorePoint.pop();
c.scale(inverseScale,inverseScale);
c.drawImage(img, 0, 0);
c.scale(drawingView.scaleFactor, drawingView.scaleFactor);
}
},
/**
* Pushes canvas into drawingView.canvasRestorePoint
*/
saveCanvas:function()
{
var canvas = document.getElementById("drawPop.canvasOne");
var urlData = canvas.toDataURL();
drawingView.canvasRestorePoint.push(urlData);
},
EXAMPLE OF USE:
readGrillInputs:function()
{
var glassNum = ir.get("drawPop.grillGlassNum").value;
var panelNum = ir.get("drawPop.grillPanelNum").value;
drawingView.restoreCanvas();
drawEngine.drawGrill(glassNum, panelNum,null);
drawingView.saveCanvas();
},
sortClick:function(event)
{
..... //Sorts where user has clicked and generates panel/glass num
.....
drawingView.showClick(panelNum, glassNum);
},
showClick:function(panelNum, glassNum)
{
var glass = item.panels[panelNum].glasses[glassNum];
var c = drawEngine.context;
drawingView.restoreCanvas();
drawingView.saveCanvas();
c.strokeStyle = "red";
c.strokeRect(glass.x, glass.y, glass.w, glass.h);
},
By just looking at the code setting the img.src is an async action to retrieve the image, so when you try to draw it 2 lines later to the canvas, it probably hasn't been loaded yet (having it in cache will make it return fast enough that it might work).
You should instead use an img.onload function to draw the image when it has loaded.
restoreCanvas:function()
{
var inverseScale = (1/drawingView.scaleFactor);
var canvas = document.getElementById("drawPop.canvasOne");
var c = canvas.getContext("2d");
if (drawingView.canvasRestorePoint[0]!=null)
{
c.clearRect(0,0,canvas.width,canvas.height);
var img = new Image();
img.onload = function() {
c.scale(inverseScale,inverseScale);
c.drawImage(img, 0, 0);
c.scale(drawingView.scaleFactor, drawingView.scaleFactor);
};
img.src = drawingView.canvasRestorePoint.pop();
}
},
I am working with the signature_pad lib to accept signature for my web app. I am having an issue after loading a saved signature.
Assuming I am working with a blank signaturePad, I create some signature and save it.
After refreshing the page, this is what shows up.
Notice it moves to the right and also seems like its zoomed in slightly.
Any idea why this may be happening? Here is some code I am working with:
<canvas class="signature-canvas" height="150" width="500" id="signatureCanvas"></canvas>
var canvas = document.querySelector("canvas#signatureCanvas");
scope.signaturePad = new SignaturePad(canvas);
scope.c2 = document.createElement('canvas');
scope.ctx = scope.c2.getContext('2d');
scope.img = new Image();
scope.img.crossOrigin = "Anonymous";
scope.img.onload = function() {
scope.drawSig();
}
scope.img.src = scope.signature;
scope.drawSig = function(){
scope.ctx.drawImage(scope.img, 0, 0);
scope.imgStr = scope.c2.toDataURL("image/png", "");
scope.signaturePad.fromDataURL( scope.imgStr );
};
I'm developing hybrid javascript app for android using cordova.
In the following code I use two ways of drawing image on canvas: with setTimeout and without.
Following code (wrapped with cordova) on android device doesn't react on func1 but does react on func2. The second click on func1 finally draws image on a canvas.
Which is completely strange.
I assume it has something to do with android device performance because on my desktop PC both functions works fine.
Why this happening? How to avoid using setTimeout?
<html style="background: white;">
<head>
</head>
<body>
<button onclick="func1()">render img2 func1</button>
<button onclick="func2()">render img2 func2</button><br />
<canvas id="canv">canv</canvas>
<script>
var img = new Image();
var canvas = document.getElementById('canv');
canvas.width = 100;
canvas.height = 100;
var ctx = canvas.getContext("2d");
function setSrc() {
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAASUlEQVRo3u3PAQ0AIAwDsIGC+TcLLkhOWgddSU6Ga5udT4iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi8cQEjUgGTmE6z3QAAAABJRU5ErkJggg=="
};
function drawImg() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
function func1() {
setSrc();
drawImg();
};
function func2() {
setSrc();
setTimeout(function () {
drawImg();
}, 500);
};
</script>
</body>
</html>
It's not so strange as image loading is asynchronous. You are trying to draw the image before it has loaded. On the second click the image has loaded so therefor it will be drawn.
You need to use a callback mechanism in order to make this work:
function setSrc(callback) {
img.onload = callback; /// when image is loaded this will be called
img.src = 'data: ...snipped...';
};
function drawImg() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
Then modify your function slightly:
function func1() {
setSrc(drawImg); /// drawImg is now callback function for setSrc.
};
Note: if you need to for example draw things on top of the image you must continue your code from the callback function. For example add a callback also for the drawImg function which calls the next step in the code after the image has been drawn. This is because as mentioned, image loading is asynchronous and if you attempt to draw anything else before the image has been loaded the image will be drawn on top instead
I'm trying to convert an image from the canvas to the image, which can be saved by right-clicking mouse.
Everything works fine, but if I put Image on the canvas (drawImage), the image is not transferred.
Image on the left is, and it is not right.
Why?
I also put an example in the sandbox. http://jsfiddle.net/qS9qP/
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
<img src="f2.ico"/>
</body>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
var img=new Image();
img.onload = function(){
ctx.drawImage(img, 10, 10);
}
img.src="http://www.cisco.com/favicon.ico"
// transfer canvas to image
document.images[0].src=document.getElementById("myCanvas").toDataURL("image/png");
</script>
toDataURL executes before the picture gets loaded. Try putting it in the onload function.
Also, you can not use images from other domains because of the Same Origin Policy, and it will throw a SecurityError:
Uncaught Error: SecurityError: DOM Exception 18
Now this will work: http://jsfiddle.net/DerekL/qS9qP/2/show/
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 10, 10);
document.images[0].src = canvas.toDataURL("image/png"); //Put it inside
}
img.src = "http://jsfiddle.net/img/logo.png" //Same domain
#Derek and #robertklep are correct:
the image is drawn after the toDataURL call; and then
SecurityError: DOM Exception 18
The cross-domain problem is somewhat beyond the scope of this question, but to demonstrate the point, this can be made to work as you expect (by eliminating the DOM Exception):
http://jsfiddle.net/c24w/E3SPv/