kristalize
kristalize

Reputation: 75

Drawing img in Canvas with for loop

var c=document.getElementById("cvs");
var ctx=c.getContext("2d");
var imgArray = [];          
for (var i=0;i<data.length;i++){
    var drawRepeat = Math.floor((data[i]/divider)); 
    imgArray[i] = [];   
    for (var j=0;j<drawRepeat;j++){
            //alert(j);
        var xPos = ((i*30)+10);
        var yPos = 250-((j*30)+30);
        imgArray[i][j] = new Image();
        imgArray[i][j].src="assets/orange.png";
        imgArray[i][j].onload = function(){
        ctx.drawImage(imgArray[i][j],xPos,yPos);
        };

    }
}

I want to draw multiple images with a for loop. weirdly when i place an alert() with the for loop it works? but if i comment it away it will only display 1 of the image.

Is there any solution to this?

Upvotes: 1

Views: 4005

Answers (3)

kristalize
kristalize

Reputation: 75

var c=document.getElementById("cvs");
        var ctx=c.getContext("2d");
        var img = new Image();
        img.src="assets/orange.png";
        img.onload = function(){
            for (var i=0;i<data.length;i++){
                var drawRepeat = Math.floor((data[i]/divider));
                for (var j=0;j<drawRepeat;j++){
                    //alert(j);
                    var xPos = ((i*30)+10);
                    var yPos = 250-((j*30)+30);
                    ctx.drawImage(img,xPos,yPos);
                }
            }
        }

I managed to solved the problem with onload by placing the onload outside the for loops and putting drawImage command inside the onload function and in the for loops. Works as well.

Upvotes: 0

Parth Thakkar
Parth Thakkar

Reputation: 5475

That's the problem related to closures. I'll explain after giving the code.

var c = document.getElementById("cvs");
var ctx = c.getContext("2d");
var imgArray = [];
for (var i = 0; i < data.length; i++) {
    var drawRepeat = Math.floor((data[i] / divider));
    imgArray[i] = [];
    for (var j = 0; j < drawRepeat; j++) {
        //alert(j);
        (function (_i, _j) {
            var xPos = ((_i * 30) + 10);
            var yPos = 250 - ((_j * 30) + 30);
            imgArray[_i][_j] = new Image();
            imgArray[_i][_j].src = "assets/orange.png";
            imgArray[_i][_j].onload = function () {
                ctx.drawImage(imgArray[_i][_j], xPos, yPos);
            };
        })(i, j);
    }
}

UPDATE:

The reason I am doing this weird stuff is that the function registered to onload event will be called asynchronously - that is the loop will continue and the function will be called when the image loads, and not at that very moment. So what happens is that this loop continues, and once it's completed (note that this may occur even before completion), i's value will be data.length - 1. Now, some image is loaded, and then the function is called. This function refers to i (in the code you gave), which by now is data.length - 1. And so, only the last image gets drawn. I know this is confusing - even I felt it confusing when I first stumbled upon closures. I recommend you read some good article on them and you'll see what the problem is.

What we did was to create a scope (experts might find a problem with this expression) by creating an anonymous function which is called immediately, and is passed the values of i and j as it's parameters - _i and _j (Note that we could've used the same names, but to avoid confusion, I didn't use the same names). Now, these are local to the function and aren't altered. So, when ith image is loaded, it's onload function is called which draws the _ith image in the array.

I have failed to explain it well, so as I said, please read some article on closures.

Upvotes: 3

danp
danp

Reputation: 15241

Preload the images, instead of using a onload handler.

Upvotes: 0

Related Questions