drawImage in JavaScript (Canvas)

1.7k views Asked by At

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)

3

There are 3 answers

1
Abdul Jabbar On BEST ANSWER

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);
    }
}
3
KaliedaRik On

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); 
        }
    }
}
2
markE On

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);
    }

}