I got a code for loading an image into a canvas, and it works fine, but when I try to iterate to do it multiple times, it only loads one image (the last one). Anyone knows why?
<pre id="output_1"></pre>
<canvas id="canvas_1"></canvas>
<pre id="output_2"></pre>
<canvas id="canvas_2"></canvas>
<script type="text/javascript">
var pics = [ 'Desert.jpg', 'Hydrangeas.jpg' ];
for(var i=0; i < pics.length; i++) {
var img = new Image();
var ctx = $( '#canvas_'+(i+1) )[0].getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.src = pics[i];
}
</script>
The var img is being overwritten as you loop. The onloads will not be called until the current execution is complete. By that time img will equal the 3rd iteration.
To fix
var pics = [ 'Desert.jpg', 'Hydrangeas.jpg' ];
function loadImage(i){
var img = new Image();
var ctx = $( '#canvas_'+(i+1) )[0].getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.src = pics[i];
}
for(var i=0; i < pics.length; i++) {
loadImage(i);
}
The call to the function loadImage creates a new reference to all the variable each time it is called. It differs from onload because it is called immediately, while onload is a callback and must wait until the currently executing context has completely exited (in other words returned until there are no more returns), then and only then can the onload happen.
Related
I have several canvases. I also have several picture URLs. I want to draw all pictures on the canvas. There is a problem in the drawing function. Drawing the image only works when the image loads completely, but I have to draw the image as it loads. I wrote following code:
for (var i = 2; i < length; i++) {
canvid[i] = "canv" + i;
img[i] = new Image();
img[i].src = "..\\images\\UploadImage\\"+ name + i + ".jpg";
img[i].onload = function () {
var c = document.getElementById(canvId[i]);
var cDraw = c.getContext("2d");
cDraw.drawImage(img[i], 0, 0);
};
I know this code has error, it's kind of pseudo code to show what I want.
Put your logic in
$(documet).ready(function(){
//logic
});
the answer is in following link
stack overflow link
when you want to call on click event on image variable you have to wait for it
so you couldn't use loop you have to put next call on previous image on load event .
var loadImages = function (imageURLarray) {
if (!(startPage < pages))
return;
canvid = "canv" + i;
img.src = imageURLarray[startPage];
// your top code
img.onload = function (e) {
// code, run after image load
var c = document.getElementById(canvid);
var cDraw = c.getContext("2d");
cDraw.drawImage(img, 0, 0);
startPage++;
loadImages(imageURLarray);
}
}
loadImages(imageURLarray);
I'm trying to make a tick method for this code.
When I try to put a while loop or time interval it just goes blank.
I want the tick method to call this function without the canvas going blank.
How would i make that tick method
function setup(){
var canvas = document.getElementById('my_canvas');
var ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
var gun = new Image();
var badguy = new Image();
var wall1 = 200;
var ground = new Image();
var back = new Image();
var back2 = new Image();
var back3 = new Image();
var wall = new Image();
var wall2 = new Image();
back.onload = function() {
ctx.drawImage(back, 0, 0, 800, 300);
};
back2.onload = function() {
ctx.drawImage(back2, mountainplace, 0, mtnsize1, 300);
};
back3.onload = function() {
ctx.drawImage(back3, mountainplace2, 0, mtnsize2, 300);
};
ground.onload = function() {
ctx.drawImage(ground, groundplace, 300, 1980, 200);
};
wall.onload = function() {
ctx.drawImage(wall, place2, 250, size2, 100);
};
wall2.onload = function() {
ctx.drawImage(wall2, place, 250, size, 100);
};
badguy.onload = function() {
ctx.drawImage(badguy, badguyplace, 250, 100, 100);
};
gun.onload = function() {
ctx.drawImage(gun, 0, 100, 400, 400);
};
back2.src = "moutain1.png";
back3.src = "moutain2.png";
back.src = "backing.png";
ground.src = "ground1.jpg";
wall.src = "wall.png";
wall2.src = "wall2.png";
badguy.src = "santa2.png";
gun.src = "gun1.png";
};
You need to clarify the question because I'm not sure what exactly you are trying to achieve.
I assume you put some code at the end of your setup() function that performs some operations on the images. But before you can do it you need to wait for the images to load.
BTW: another problem with your code is that the images will be drawn on the canvas in the order in which they load, which may be unpredictable. You probably want to avoid this too.
A solution to your problem (or at least to what I think your problem is) is to first start loading the images and then only perform further operations after they have all been loaded.
You can use the following code to do this:
function makeAllLoadedHandler(image_files_count, on_all_loaded) {
return function() {
--image_files_count;
if (image_files_count == 0) {
// All images loaded, call the function.
on_all_loaded();
}
}
}
function loadAllImages(image_files, on_all_loaded) {
var images = {};
var callback = makeAllLoadedHandler(image_files.length, function() { on_all_loaded(images); } );
for (var i = 0; i < image_files.length; ++i) {
var image = new Image;
image.src = image_files[i];
image.onload = callback;
images[image_files[i]] = image;
}
}
The loadAllImages() function takes an array of image file names and a function to call when all the images have been loaded.
You can use it like this in your code:
function setup() {
var image_files = [
"mountain1.png",
"mountain2.png",
"backing.png",
"ground1.jpg",
"wall.png",
"wall2.png",
"santa2.png",
"gun1.png" ];
loadAllImages(image_files, onAllImagesLoaded);
}
function onAllImagesLoaded(images) {
// Draw your images and perform all the other tasks on them.
// The 'images' object stores each of the Image object under a key that is
// its file name.
ctx.drawImage(images['backing.png'], 0, 0, 800, 300);
// ...
// Do other stuff with the images.
}
You just call the setup() function like you used to and then the onAllImagesLoaded function will be called some time later when all the images are available. You continue your processing in there.
I hope this helps. Although it's possible that your problem is completely different ;)
I have an array of enemies sent from the server and I am recreating them because they were serialized. After, I'm trying to get them to render on the canvas, but that isn't working for some reason.
for (var i = 0; i < enemies.length; i++) { // recreate each enemy and render it
var image = new Image();
var currentFish = new Fish();
for (var key in enemies[i]) { // copying properties to object that has the necessary methods
if (enemies[i].hasOwnProperty(key)) {
currentFish[key] = enemies[i][key];
}
}
image.src = currentFish.icon;
image.onload = function () {
ctx.drawImage(image, currentFish.position.x, currentFish.position.y);
};
ctx.fillText('Drone', 250, 200);
}
I think the issue is that image.onload is not called until after or in between frames so it isn't seen. I'm not sure how to work around this.
Edit:
I forgot to mention that I'm using requestAnimationFrame to handle rendering the canvas, so I don't know when the frame is going to be rendered.
The problem is that your outside for loop is overwriting the image variable with each loop (before the image can be loaded and drawn).
Try this alternative image loader which preloads all images into imgs[] and then calls start():
// image loader
var imageURLs = []; // put the paths to your images here
var imagesOK = 0;
var imgs = [];
imageURLs.push("");
loadAllImages(start);
function loadAllImages(callback) {
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function() {
imagesOK++;
if (imagesOK >= imageURLs.length) {
callback();
}
};
img.onerror = function() {alert("image load failed");}
img.crossOrigin = "anonymous";
img.src = imageURLs[i];
}
}
function start() {
// imgs[] holds fully loaded images
// imgs[] is in the same order as imageURLs[]
}
I have an array of image objects which hold all the necessary info like path,x, y, w, h. Now i want to draw all those images on canvas in a loop.. but when i do so, it only draws the first image. Here is the code:
for(var i=0; i<shapes.length; i++)
{
var A = shapes.pop();
if(A.name == 'image' && A.pageNum == pNum)
{
var img = new Image();
img.src = A.path;
img.onload = function() {
context.drawImage(img, A.x, A.y, A.w, A.h);
}
}
}
i checked all the info in the shapes array... inside the if condition, before calling drawImage function, all the info of each image is correct but for some strange reason it doesn't display images except the 'last' in the array (the one which pops out last)
Your image loading code is faulty.
Each image will take time to load and by then you have overwritten var img with another new Image();
Here's an example of an image loader that executes only after all the images have been loaded and are ready to be drawn:
[ Warning: untested code -- some adjustments may be required! ]
// image loader
var imageURLs=[]; // put the paths to your images in this array
var imagesOK=0;
var imgs=[];
imageURLs.push("");
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
// note: instead of this last line, you can probably use img.src=shapes[i].path;
}
}
function start(){
// the imgs[] array holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
context.drawImage(imgs[i],shape.x,shape.y,shape.w,shape.h);
}
}
It was the problem of some sort of closure... like, the loop finishing before images getting loaded or something. The solution that worked for me was putting all the image loading code in a separate function and calling that function from the loop:
if(A.name == 'image' && A.pageNum == pNum)
{
displayImage(A.path, A.x, A.y, A.w, A.h);
}
function displayImage(path, x, y, w, h)
{
var img = new Image();
img.src = path;
img.onload = function() {
context.drawImage(img, x, y, w, h);
}
}
I don't understand why you're using pop() to get your object data. You could instead access each object using shapes[i] notation and, as a bonus, store image handles in each object:
for(var i=0; i<shapes.length; i++)
{
if(shapes[i].name == 'image' && shapes[i].pageNum == pNum)
{
shapes[i].img = new Image();
shapes[i].img.src = shapes[i].path;
shapes[i].img.onload = function() {
context.drawImage(shapes[i].img, shapes[i].x, shapes[i].y, shapes[i].w, shapes[i].h);
}
}
}
The below handleFiles method is being passed files from both drag and drop and a file input. After it gets the data url for a given file it passes it to the processImage function. This function creates a new image and sets the src and file for that image. I then take different actions based on the width of the incoming image and insert the image into the dom. However, I noticed when dropping in a bunch of images imageWidth will get set to 0. I have confirmed the image.src is correctly set and that dropping the same image in by itself works fine. I also have confirmed that if I remove the width calculations the image does display correctly on the page. When I enter the debugger I can confirm that immediately after imageWidth is set to 0 i.width returns a correct value. At first I thought it might be a threading issue in Chrome, but then when I saw it happen in FireFox as well I became alarmed. I have not been able to pinpoint a certain threshold, but the more load you put on the browser the less likely it is to correctly get the width.
I am seeing the problem in both FireFox 16.0.2 and Chrome 23.0.1271.95.
function handleFiles(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
if( !isImage(file) ) {
continue;
}
var reader = new FileReader();
reader.onloadend = function(e) {
var dataURL = e.target.result;
processImage(file, dataURL);
}
reader.readAsDataURL(file);
}
}
function processImage(file, dataURL) {
var i = new Image();
i.src = dataURL;
i.file = file;
//console.log(i);
var maxWidth = 600;
var imageWidth = i.width;
......
}
As with all images, they may need time to load before they will tell you their width:
var i = new Image();
i.onload = function() {
//console.log(i);
var maxWidth = 600;
var imageWidth = this.width;
}
i.src = dataURL;
i.file = file;
The width (and height) might be 0 because it's not loaded yet.
Try adding the load event like so:
function processImage(file, dataURL) {
var i = new Image();
i.addEventListener("load", function () {
var maxWidth = 600;
var imageWidth = i.width;
......
});
i.src = dataURL;
i.file = file;
}