2

I'm simply trying to load 3 different images side by side, but when I run the following code it only draws the last image I push into src[] multiple times.

function draw(){
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    var tileWidth = 25;
    var tileWCount = 3;
    var multi = 2;
    var src = [];
    src.push("http://st.hzcdn.com/simgs/6361d7eb0dee6b28_4-1763/contemporary-tile.jpg");
    src.push("http://www.in.all.biz/img/in/catalog/middle/205131.jpeg");
    src.push("http://www.magnet-textures.com/textures/2011/7/715-1259-1-blue-tiles-ico-big.jpg");

    for(var i=0; i < tileWCount; i++){
        var img = new Image();
        img.onload = (function(value){
            return function() {
                ctx.drawImage(img, (value * tileWidth * multi), 0, tileWidth * multi, tileWidth * multi);
            }
        })(i);
        img.src = src[i];
    }
}

I took a big chunk of this code from this Stack Overflow post. I can set the src to a single image but when I try to use my iterator it starts failing. I did notice that the answer there is using ctx.drawImage with src[value] but I couldn't get a single thing to draw with that.

I'm at a complete loss as to why this might be happening.

I've set up a jsfiddle project for convenience: http://jsfiddle.net/fierstarter/jv0ko966/

function draw(){
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    var tileWidth = 25;
    var tileWCount = 3;
    var multi = 2;
    var imgA = [];
    var src = [];
    src.push("http://st.hzcdn.com/simgs/6361d7eb0dee6b28_4-1763/contemporary-tile.jpg");
    src.push("http://www.in.all.biz/img/in/catalog/middle/205131.jpeg");
    src.push("http://www.magnet-textures.com/textures/2011/7/715-1259-1-blue-tiles-ico-big.jpg");
    
    for(var i=0; i < tileWCount; i++){
        var img = new Image();
        img.onload = (function(value){
            return function() {
                ctx.drawImage(img, (value * tileWidth * multi), 0, tileWidth * multi, tileWidth * multi);
            }
        })(i);
        img.src = src[i];
    }
}

draw();
#canvas{
    border: 1px solid black;
}
<canvas id="canvas" width="320" height="240"></canvas>
Community
  • 1
  • 1
Fier
  • 342
  • 1
  • 4
  • 11

2 Answers2

3

You will need to use:

ctx.drawImage(this, (value * tileWidth * multi), 0, tileWidth * multi, tileWidth * multi);

instead of

ctx.drawImage(img, (value * tileWidth * multi), 0, tileWidth * multi, tileWidth * multi);

Your img variable suffers from the same "last value" problem that you fixed for i using your closure. You could capture the value of img in a closure in the same way, but it's easier to just use this.

Working Live Demo:

function draw(){
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    var tileWidth = 25;
    var tileWCount = 3;
    var multi = 2;
    var imgA = [];
    var src = [];
    src.push("http://st.hzcdn.com/simgs/6361d7eb0dee6b28_4-1763/contemporary-tile.jpg");
    src.push("http://www.in.all.biz/img/in/catalog/middle/205131.jpeg");
    src.push("http://www.magnet-textures.com/textures/2011/7/715-1259-1-blue-tiles-ico-big.jpg");
    
    for(var i=0; i < tileWCount; i++){
        var img = new Image();
        img.onload = (function(value){
            return function() {
                ctx.drawImage(this, (value * tileWidth * multi), 0, tileWidth * multi, tileWidth * multi);
            }
        })(i);
        img.src = src[i];
    }
}

draw();
#canvas{
    border: 1px solid black;
}
<canvas id="canvas" width="320" height="240"></canvas>

JSFiddle Version: http://jsfiddle.net/jv0ko966/2/

Maximillian Laumeister
  • 19,884
  • 8
  • 59
  • 78
  • I'm pretty new to this, why does 'this' work and not 'img'? (And it did work, thank you) – Fier Aug 21 '15 at 20:11
  • 1
    In your onload function, `img` refers to the variable declared in the for loop. Since the for loop will have finished running by the time the onload functions are called, the value of `img` will be its value in the last iteration of the for loop - meaning your final image. By using `this`, we are referring to the image that that particular `onload` is getting called from, which is what we want. For more information on closures inside for loops, see [this answer](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example). – Maximillian Laumeister Aug 21 '15 at 20:14
  • Thanks for expanding the answer! – Fier Aug 21 '15 at 20:14
0

use src.length in for loop instead of taking statically defined variable (tileCount). I tried it, its working.

  • I took a snippet of a project I'm working on just to have a 'working' demo to post. – Fier Aug 21 '15 at 20:16