I am working on a website that, when loaded, it displays the compiled image from an HTML canvas element. My problem is that I can't tell when an image is loaded to draw it to the canvas or how to force an image to be loaded before I proceed to the next step. I have been looking through previous posts about the subject and have tried a lot of different solutions but nothing is working for me.
I am using web2py so I do use some Python helpers which are run on the server side.
When I use this:
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
image1 = new Image();
image1.src = '{{=URL('static', 'images/image-wrap-border-land.png')}}'; //resolves to: '/SNotes/static/images/image-wrap-border-land.png'
image1.onload = function() {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
};
Since the image isn't loaded, the onload function doesn't do anything and it skips over it. I have also tried putting the onload function before I set the source but the image still isn't drawn.
I have tried:
//code from above
if(image1.completed) {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
}
But have faced the same problem.
I have also looked at the possibility that the image is producing an error as it is loaded. To catch this I wrote:
//code from above
image1.onerror = function() {
console.log('error');
}
But the image source is fine, and the image isn't producing an error, it is just taking a long time to load.
Is there a way to wait for an image to load before it is drawn to the canvas?
Edit to add specificity:
My HTML looks like this:
<div id="profile-container" class="container-fluid">
<h1>My Notes</h1>
<div id="empty-orders" class="column-wrapper disable" style="padding-top: 5px; margin: 0 38%;">
<h3>Create a Note and it will appear here</h3>
<button class="btn btn-lg btn-outline">{{=A('GET STARTED', _href=URL('default', 'canvas_board_print'))}}</button>
</div>
<div id="loading" class="column-wrapper">
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
</div>
<div id="row1" class="row">
</div>
</div>
My javascript looks like this:
function showOrders(note_orders) {
var orders = note_orders;
//console.log(orders);
var row_num = 1;
var node_num = 1;
if(orders.length > 0) {
for (var i = 0; i != orders.length; i++) {
orders[i].border = JSON.parse(orders[i].border);
orders[i].image_retouching = JSON.parse(orders[i].image_retouching);
orders[i].shipping_info = JSON.parse(orders[i].shipping_info);
var new_node = $("<div>", {id: "node" + node_num, "class": "col-xs-3 node-wrapper"});
var new_canvas = $('<canvas>', {id: "canvas" + node_num, style: 'display: none'});
var new_image = $('<img>', {id: "note_prev" + node_num, 'class': 'img-responsive'});
$('#row' + row_num).append(new_node);
$('#node'+ node_num).append(new_canvas).append(new_image).append(processOrders(orders[i], node_num));
node_num++;
if (i != 0 && (i + 1) % 4 == 0) {
row_num++;
var new_row = $("<div>", {id: "row" + row_num, "class": "row"});
$(' #profile-container ').append(new_row);
}
}
$(' #loading ').addClass('disable');
} else {
$(' #loading ').addClass('disable');
$(' #empty-orders ').removeClass('disable');
}
}
function processOrders(curr_order, node_num) {
var canvas = document.getElementById('canvas' + node_num);
var context = canvas.getContext('2d');
var image1 = new Image();
image1.src = curr_order.image_url;
canvas.width = image1.naturalWidth;
canvas.height = image1.naturalHeight;
if(image1.complete) {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
if(curr_order.border.style == 'color_wrap') {
document.getElementById('note_prev' + node_num).style.border = "10px solid " + curr_order.border.color;
} else if(curr_order.border.style == 'image_wrap') {
var image_wrap = new Image();
if(canvas.width > canvas.height) {
image_wrap.src = '{{=URL('static', 'images/image-wrap-border-land.png')}}';
} else {
image_wrap.src = '{{=URL('static', 'images/image-wrap-border-port.png')}}';
}
console.log(image_wrap);
image_wrap.onload = function() {
context.drawImage(image_wrap, 0, 0, canvas.width, canvas.height);
};
image_wrap.onerror = function() {
console.log('errors');
}
}
}
document.getElementById('note_prev' + node_num).src = canvas.toDataURL('image/png');
var node_div = document.createElement('div');
//Other non-image informantion is appended to node_div
return node_div;
}
My problem is when drawing image_wrap, not image1 from my above example. Image1 from my code draws fine, but image_wrap won't appear when it is drawn to the canvas.
I FIXED IT!!!
I moved the creation of image_Wrap variable up with the creation of the image1 variable.
function processOrders(curr_order, node_num) {
var canvas = document.getElementById('canvas' + node_num);
var context = canvas.getContext('2d');
var image1 = new Image();
var image_wrap = new Image();
image1.src = curr_order.image_url;
canvas.width = image1.naturalWidth;
canvas.height = image1.naturalHeight;
if(image1.complete) {
context.drawImage(image
I thought this wouldn't be a good idea because I didn't want to create extra variables even if they weren't going to get used, but I guess it fixed it.
I was able to load the image using your code.
Can you confirm you are not getting an error while loading the image?
(Seeking clarification: posted as an answer to demo the running code snippet)
Update: added a hackish way to wait a second after loading image and before drawing it to canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var image1 = new Image();
image1.src = 'http://lorempixel.com/400/200/sports/1/';
image1.onload = function() {
// you could draw the image right away
// ctx.drawImage(image1, 0, 0, canvas.width, canvas.height);
// OR
// You could wait for an extra second before drawing the image (hackish, I know)
setTimeout(function() {
ctx.drawImage(image1, 0, 0, canvas.width, canvas.height);
}, 1 * 1000); // 1 second (in milliseconds)
};
<h2>canvas</h2>
<canvas id='canvas'>
</canvas>
<hr/>
Set the onload function BEFORE you set the src. It's likely that the image is loading before the onload function is being set.
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
image1 = new Image();
image1.onload = function() {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
};
image1.src = '{{=URL('static', 'images/image-wrap-border-land.png')}}'; //resolves to: '/SNotes/static/images/image-wrap-border-land.png'
The first parameter for context.drawImage is the image you want to draw, the second two are the position on the context....
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
The remaining parameters allow you to scale the image.
Suggest you try..
image1.onload = function() { context.drawImage(image1,0,0); }
Related
I'm importing an image (that has to come in portrait) onto a canvas object, rotating it (because it's actually landscape) and running some OCR on the base64 from the canvas. That all works fine. However when I put a new image onto the canvas, the old one is retained and never replaced. I've tried clearRect, even gone to the extent of creating a new dom element each time and destroying it when I have everything I need (which is still in the code below) but I just cannot get the first image to clear.
Here's the relevant bit of the code
function onSuccess(imageURI) {
//CREATE NEW CANVAS ELEMENT
g = document.createElement('canvas');
g.setAttribute("id", "thePic");
g.style.overflow = "visible";
document.body.appendChild(g);
const canvas = document.getElementById('thePic'),
context = canvas.getContext('2d');
make_base();
function make_base()
{
base_image = new Image();
base_image.src = imageURI;
base_image.onload = function(){
const uriheight = base_image.naturalHeight;
const uriwidth = base_image.naturalWidth;
console.log(uriwidth + " " + uriheight);
context.canvas.width = uriheight;
context.canvas.height = uriheight;
context.drawImage(base_image, 0, 0);
//ROTATE THE IMAGE 90
context.clearRect(0,0,canvas.width,canvas.height);
context.save();
context.translate(canvas.width/2,canvas.height/2);
context.rotate(90*Math.PI/180);
context.drawImage(base_image,-base_image.width/1.2,-base_image.width/1.2);
context.restore();
var theCanvas = document.getElementById('thePic');
var rotImg = theCanvas.toDataURL();
var rotImg = rotImg.substring(21);
textocr.recText(4, rotImg, onSuccess, onFail);
function onSuccess(recognizedText) {
var recdata = recognizedText.lines.linetext;
var blockData = recognizedText.blocks.blocktext;
context.clearRect(0, 0, 10000,10000);
base_image = "";
rotImg = "";
document.getElementById('thePic').outerHTML = "";
document.getElementById('cgh').innerHTML = "";
}
}
}
Any advice much appreciated.
So it turned out to not be the canvas at all, it was image caching in a previous function that give the image to the canvas.
Thanks to everyone who took the time to have a squiz.
In my current code I have different functions to draw different canvases, these work perfectly. The draw function will be called a number of times and store the result to be called later, once the image is finalised.
I cannot get the two images to merge together. I can redraw them individually in mergeCanvas(), by calling 'testbor1.url' but when I try to draw them onto a new canvas, the result is blank.
Can anyone tell me why I cannot draw these onto one canvas?
Following: How can I put multiple canvas elements in to one canvas element?
function draw()
{
var can3 = document.getElementById('RugCanvas3');
can3.offset = 0;
can3.width = namespacetest.getCanvasWidth();
can3.height = namespacetest.getCanvasHeight();
var ctx3 = can3.getContext('2d');
ctx3.drawImage(drawBorder(70,size,img, borderwidth), 0, 0);
ctx3.drawImage(drawOverlay(borderwidth, 70), 0, 0);
var img = new Image();
img.src = can3.toDataURL("image/png");
namespacetest.dataURLs['img1'] = img.src;
}
namespacetest.mergeCanvas = function()
{
var testbor1 = new Image();
var testbor2 = new Image();
testbor1.src = namespacetest.dataURLs['img1'];
testbor2.src = namespacetest.dataURLs['img2'];
var bor = document.getElementById('Canvas5');
var ctx = bor.getContext("2d");
ctx.drawImage(testbor1, 0, 0, 400, 400);
ctx.drawImage(testbor2, 0, 0, 400, 400);
var buffer_img = new Image();
buffer_img.src = bor.toDataURL("image/png");
var output = document.getElementById('RugCanvas6');
output.innerHTML = '<img src="' + buffer_img.src + '" alt="Canvas Image" />';
}
I develop Photoshop extension that sends images to the server.
Edit: Extensions in photoshop build from html file that define the GUI, js file that basically is the same as any js file, but it's can also launch photoshop function and it is execute from photoshop.
I need to send the images from the file system of the user (from C:\path\to\images)
To encode the images I converted them to dataURL (base64).
The problem occurs in the first time that I convert the images to dataURL. But in the second time and so, it manages to convert the images and everything is fine. In the first time the image doesn't loaded.
I have a folder where the images are and I want to upload the pictures from there, I used a loop that runs on photos and set them into <img src=path> to and then it converts them based 64 via <canvas>.
My code:
function convertLayersToBase64(imgHeight, imgWidth){
var img = new Image();
images = [];
for (var i=0; i<=imagesLength; i++){
path = folder + "layer " + i +".png";
img.src = path;
var canvas = document.createElement("canvas");
canvas.height = imgHeight;
canvas.width = imgWidth;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL();
images.push( dataURL );
}
return images;
}
I tried to delay the conversion by delay:
function delay(time) {
var d1 = new Date();
var d2 = new Date();
while (d2.valueOf() < d1.valueOf() + time) {
d2 = new Date();
}
}
JQuery when ready:
$(function(){
images.push(getBase64Image());
});
Img.complete
while(!img.complete)
continue;
(In the last example the code stuck in loop)
To put the function in:
img.onload = function(){
//the function here..
//when I use this method it succeed to convert
//only the last image.
}
Nothing worked..
I tried everything, please tell me what to change and how to fix that.
Edit: It's seem to me that the only way to load an image it's when the code
The function onload is an asynchronous action. You cannot just return images as the last statement within your convertLayersToBase64 function. You should either use promises, or a more simple approach would be to use a callback function.
function convertLayersToBase64(imgHeight, imgWidth, callback){
var images = [];
for (var i = 0; i <= imagesLength; i++) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.height = imgHeight;
canvas.width = imgWidth;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL();
images.push(dataURL);
if(images.length === imagesLength) {
callback(images);
}
}
path = folder + "layer " + i +".png";
img.src = path;
}
}
You would call this like:
convertLayersToBase64(200, 200, function(images) {
console.log('hii, i got images', images);
});
This is obviously without any form of error check, or even best practice guidelines, but I'll leave it up to you to implement that.
I want to draw two images on the same canvas. The first image is background.jpg and the second is photo.jpg. I want the photo.jpg always on top of the other:
var ctx = document.getElementById("main").getContext("2d");
var background = new Image();
var photo = new Image();
background.onload = function() {
ctx.drawImage(background, 0, 0);
}
photo.onload = function() {
ctx.drawImage(photo, 0, 0);
}
background.src = "background.jpg";
photo.src = "photo.jpg"
My question is how can I make sure the photo is always on the top. Because the onload are callbacks, I cannot make any assumptions about the calling order. Thanks!
Store your images in an array instead. That will makes sure the order is kept no matter which image finish loading first:
var ctx = document.getElementById("main").getContext("2d");
var background = new Image();
var photo = new Image();
var images = [background, photo]; /// the key
var count = images.length;
background.onload = photo.onload = counter;
background.src = "background.jpg";
photo.src = "photo.jpg"
/// common loader keeping track if loads
function counter() {
count--;
if (count === 0) drawImages();
}
/// is called when all images are loaded
function drawImages() {
for(i = 0; i < images.length; i++)
ctx.drawImage(images[i], 0, 0);
}
(the draw method assumes all being drawn at position 0,0 - of course, change this to meet your criteria).
The foreground could be loaded in the callback for the background
var ctx = document.getElementById("main").getContext("2d");
var background = new Image();
var photo = new Image();
background.onload = function() {
ctx.drawImage(background, 0, 0);
photo.src = "photo.jpg" // after background is loaded, load foreground
}
photo.onload = function() {
ctx.drawImage(photo, 0, 0);
}
background.src = "background.jpg";
I have a problem displaying one canvas to another. I do everything according to this solution
<script>
var source = document.getElementById('hiddenCanvas');
var source_ctx = source.getContext('2d');
var img = new Image();
img.onload = function(){
source.width = img.width;
source.height = img.height;
source_ctx.drawImage(img, 0, 0, img.width, img.height);
}
img.src = 'icon.jpg';
var destination = document.getElementById('visibleCanvas');
var destin_ctx = destination.getContext('2d');
destin_ctx.drawImage(source, 0, 0);
</script>
Well, first canvas element displays picture correctly, but whatever I do, the second canvas does not want to display a picture.
<script>
function init()
{
var source = document.getElementById('hiddenCanvas');
var source_ctx = source.getContext('2d');
var destination = document.getElementById('visibleCanvas');
var destin_ctx = destination.getContext('2d');
var img = new Image();
img.onload = function(){
source.width = img.width;
source.height = img.height;
source_ctx.drawImage(img, 0, 0, img.width, img.height);
destin_ctx.drawImage(source, 0, 0);
}
img.src = 'arun.jpg';
}
</script>
</head>
<body onload="init();">
<canvas id="hiddenCanvas" />
<canvas id="visibleCanvas" />
Your code is not working because you are trying to access canvas element before it is loaded to dom
The way you've currently structured the code, img.onload is executed after the destin_ctx.drawImage line. That means that your program flow currently looks something like this:
Image is told to start loading
Destination canvas is drawn using (currently blank) source canvas
Image finishes loading
Image's onload executes, causing the source canvas to be drawn to. The destination canvas is NOT updated, because the destin_ctx.drawImage operation is a one-time copy.
What you need to do is move the destin_ctw.drawImage call to a place in the execution flow where you know the source canvas will definitely contain the appropriate contents. In this simple case, moving it to inside the image's onload would work.
Here's a full (but simplified) HTML file that works for me in Chromium, with a changed image url:
<script>
function load() {
var source = document.getElementById('hiddenCanvas');
var source_ctx = source.getContext('2d');
var destination = document.getElementById('visibleCanvas');
var destin_ctx = destination.getContext('2d');
var img = new Image();
img.onload = function(){
source.width = img.width;
source.height = img.height;
source_ctx.drawImage(img, 0, 0, img.width, img.height);
destin_ctx.drawImage(source, 0, 0);
}
img.src = 'ship360_32.png';
}
</script>
<body onload="load()">
<canvas id="hiddenCanvas"></canvas>
<canvas id="visibleCanvas"></canvas>
</body>
You are trying to draw the image from source before the image is loaded and the source even have the image.
Move the last draw operation inside the onload handler. Also remember to set the size for destination canvas:
var source = document.getElementById('hiddenCanvas');
var source_ctx = source.getContext('2d');
var destination = document.getElementById('visibleCanvas');
var destin_ctx = destination.getContext('2d');
var img = new Image();
img.onload = function(){
source.width = img.width;
source.height = img.height;
source_ctx.drawImage(img, 0, 0, img.width, img.height);
destination.width = source.width;
destination.height = source.height;
destin_ctx.drawImage(source, 0, 0);
}
img.src = 'icon.jpg';