Robert Robert - 2 months ago 6
Javascript Question

Why would array of images not be drawing in HTML5 Canvas?

I am trying to draw a tiled map using canvas. There are three types of tiles that need to be drawn to the canvas, so rather than call the function three times for each individual image, I'm passing an array of the images as a parameter, looping through the array, and telling canvas to draw each image. However, when I do this, I'm getting a blank canvas with no error messages being returned. When I don't pass in an array but make the function call manually for each image, the images are drawn to the canvas. Can anyone explain to me what I'm doing wrong?

window.onload = function(){
var basemap = document.getElementById("basemap");
var basemapCtx = basemap.getContext("2d");
initMap(Map.map, basemapCtx, basemap);
}

function initMap(map, ctx, canvas){
var deepWater = new Image();
var shallowWater = new Image();
var coastalWater = new Image();
deepWater.src = "deepWater.png";
shallowWater.src = "shallowWater.jpg";
coastalWater.src = "coastalWater.jpg";
//Does not draw the images
drawMap(map, [deepWater, shallowWater, coastalWater], ctx, canvas, [-2, -1, 0]);
//Does draw the images
//drawMap(map, deepWater, ctx, canvas, -2);
//drawMap(map, shallowWater, ctx, canvas, -1);
//drawMap(map, coastalWater, ctx, canvas, 0);
}

function drawMap(map, image, ctx, canvas, pos){
var screenWidth = canvas.width;
var screenHeight = canvas.height;
var tileWidth = 0;
var tileHeight = 0;
var xPos = 0;
var yPos = 0;
for(var i = 0; i < image.length; i++){
image[i].onload = function(){
for(var rows = 0; rows < map.length; rows++){
tileHeight = (screenHeight / map.length);
for(var cols = 0; cols < map[rows].length; cols++){
tileWidth = (screenWidth / map[rows].length);
if(map[rows][cols] == pos[i]){
ctx.drawImage(image[i], xPos, yPos, tileWidth, tileHeight);
}
xPos += tileWidth;
}
xPos = 0;
yPos += tileHeight;
}
yPos = 0;
tileWidth = 0;
tileHeight = 0;
}
}
}

Answer

The onload event will not fire until the current function has returned and Javascript is doing nothing. The for loop is using the variable i. When the onload events fire the value of i will be image.length one past the last image.

You need to make the variables for each onload event unique to that call. You can use closure to do that.

Change the code as follows

function drawMap(map, image, ctx, canvas, pos){

    function setOnLoad(i){  // this function creates closure over the variable i so
                            // that it is unique each time this function and the onload
                            // function runs.
        var screenWidth = canvas.width;
        var screenHeight = canvas.height;
        var tileWidth = 0;
        var tileHeight = 0;
        var xPos = 0;
        var yPos = 0;
        // the following function closes over all the variables in this function
        image[i].onload = function(){
            for(var rows = 0; rows < map.length; rows++){
                tileHeight = (screenHeight / map.length);
                for(var cols = 0; cols < map[rows].length; cols++){
                    tileWidth = (screenWidth / map[rows].length);
                    if(map[rows][cols] == pos[i]){
                        ctx.drawImage(image[i], xPos, yPos, tileWidth, tileHeight);
                    }
                    xPos += tileWidth;
                }
                xPos = 0;
                yPos += tileHeight;
            }
        }
    }
    for(var i = 0; i < image.length; i++){
        setOnLoad(i);
    }
}

Thus you will have a unique set of variables each time the onload function is called.

Comments