Aaron Wickens Aaron Wickens - 7 days ago 6
Javascript Question

Issue drawing multiple Images on Canvas with drawImage()

I just cannot get this to work for me.

I'm trying to use drawImage() to draw multiple images to a canvas. I feel like I'm massively overlooking something little.

It should draw 18 cards to the canvas. It'll start 50px in from the left, and down from the top, and draw each card 100w*150h. There should be 25px between each card image. Canvas dimensions are set to 825w * 600h.

Trying to accomplish this with plain Javascript (no jQuery). Any help is appreciated.

The image is how it is currently drawing to my canvas.
How it is outputting to the canvas currently.

// Draw the cards to the canvas.
function drawCards()
{
// Starting positions.
var x = 50;
var y = 50;

// Counter that will hold current card position.
var cardCount = 0;

var img = new Image(100, 150);

// How many rows.
for (var row = 0; row < 3; row++)
{
// How many columns.
for (var column = 0; column < 6; column++)
{
// Store the current card.
var card = memoryDeck[cardCount];

// Check if the card is flipped, if it is set an image with url to face card.
if (card.flipped == true)
{
img.onload = function() {
ctx.drawImage(this, x, y, 100, 150);
}
img.src = card.faceImage;
}
// Otherwise set image url to back of card.
else
{
img.onload = function() {
ctx.drawImage(this, x, y, 100, 150);
}
img.src = card.backImage;
}

// Increase the x position (the width of a card, plus the space in between), and the current card position being stored.
x += 125;
cardCount++;
}

// We are on a new row, reset the column card position and increase the row card position.
x = 50;
y += 175;
}
}

Answer

JS variables are not scoped to block, they are scoped to function. So when img.onload accesses x and y, they reference the final values of x and y. To create block-scoped variables, use let statements or IIFEs.

// Draw the cards to the canvas.
function drawCards()
{
    // Starting positions.
    var x = 50;
    var y = 50;

    // Counter that will hold current card position.
    var cardCount = 0;

    var img = new Image(100, 150);

    // How many rows.
    for (var row = 0; row < 3; row++)
    {
        // How many columns.
        for (var column = 0; column < 6; column++)
        {
            // Store the current card.
            var card = memoryDeck[cardCount];

            // Check if the card is flipped, if it is set an image with url to face card.
            if (card.flipped == true)
            {
                (function(x, y) {
                img.onload = function() {
                    ctx.drawImage(this, x, y, 100, 150);
                }
                img.src = card.faceImage;
                })(x, y);
            }
            // Otherwise set image url to back of card.
            else
            {
                (function(x, y) {
                img.onload = function() {
                    ctx.drawImage(this, x, y, 100, 150);
                }
                img.src = card.backImage; 
                })(x, y);          
            }

            // Increase the x position (the width of a card, plus the space in between), and the current card position being stored.
            x += 125; 
            cardCount++;
    }

        // We are on a new row, reset the column card position and increase the row card position.
        x = 50;
        y += 175;
     }

}

A similar simpler problem is this:

READ THE SOURCE CODE!
<script>
  function demo1() {
  for (var i = 0; i <= 5; i++) {
    setTimeout(function(){alert('i === ' + i)}, i * 200);
  }
  // i === 6
    
    
  // You expect this to happen:
  // [alerts "i === 1"]
  // [alerts "i === 2"]
  // [alerts "i === 3"]
  // [alerts "i === 4"]
  // [alerts "i === 5"]
  
  // What actually happens:
  // [alerts "i === 6"]
  // [alerts "i === 6"]
  // [alerts "i === 6"]
  // [alerts "i === 6"]
  // [alerts "i === 6"]
  }
</script>
<button onclick="demo1();">Demo 1 </button>
<script>
  function demo2(){
    for (var i = 0; i <= 5; i++) {
      // IIFE for the win!
      (function(i) {
      setTimeout(function(){alert('i === ' + i)}, i * 200);
      })(i);
    }
    
    // Expected:
    // [alerts "i === 1"]
    // [alerts "i === 2"]
    // [alerts "i === 3"]
    // [alerts "i === 4"]
    // [alerts "i === 5"]
    
    // Actual
    // [alerts "i === 1"]
    // [alerts "i === 2"]
    // [alerts "i === 3"]
    // [alerts "i === 4"]
    // [alerts "i === 5"]
  }
 </script>
<button onclick="demo2();">Demo 2</button>

Comments