使用drawImage()在画布上绘制多个图像

时间:2016-11-28 00:45:49

标签: javascript html5 canvas

我无法让这个为我工作。

我试图使用drawImage()将多个图像绘制到画布上。我觉得我大肆俯视一些东西。

应该在画布上画18张牌。它将从左侧开始向上50px,从顶部向下开始,并将每张卡拉出100w * 150h。每张卡片图像之间应该有25像素。画布尺寸设置为825w * 600h。

尝试使用普通的Javascript(没有jQuery)来实现这一点。任何帮助表示赞赏。

图像是当前绘制到画布的方式。 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;
    }
}

2 个答案:

答案 0 :(得分:0)

JS变量没有作用域的范围,它们的范围是功能。因此,当img.onload访问xy时,它们会引用x和y的最终值。  要创建块范围的变量,请使用let语句或IIFE。

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

}

类似的一个更简单的问题是:

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"]
  // [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 === 0"]
    // [alerts "i === 1"]
    // [alerts "i === 2"]
    // [alerts "i === 3"]
    // [alerts "i === 4"]
    // [alerts "i === 5"]
    
    // Actual:
    // [alerts "i === 0"]
    // [alerts "i === 1"]
    // [alerts "i === 2"]
    // [alerts "i === 3"]
    // [alerts "i === 4"]
    // [alerts "i === 5"]
  }
 </script>
<button onclick="demo2();">Demo 2</button>

答案 1 :(得分:0)

给定的答案是有问题的,因为它在循环中声明函数调用。这种声明模式很危险,如果您不理解关闭,可能会导致内存泄漏。用于克服范围问题的方法也是非常低效的。并且代码将不起作用,因为只创建了一个图像,并且每次设置src时,先前的加载src被取消并且该过程再次开始。

同样创建匿名函数会使调试代码变得更加困难,因为任何错误都会导致很难跟踪跟踪,并且分析也很难阅读。始终为功能命名,并始终将功能放在其范围的顶部。

function drawCards(){
    // declare all variables and functions
    var row, column, src, cardIndex, card;
    function loadThenDisplayImage (src, x, y) {
        var image;
        function onImageload () {
             ctx.drawImage(this, x, y, cardW, cardH);
        }
        image = new Image(cardW, cardH);
        image.src = src;
        image.onload = onImageLoad;
    }
    // define constants and variables.
    const left = 50;
    const top = 50;
    const cardW = 100;
    const cardH = 150;
    const xSpacing = 25;
    const ySpacing = 25;
    cardIndex= 0;

    // function logic 
    for (row = 0; row < 3; row += 1) {
        for (column = 0; column < 6; column += 1) {
            card = memoryDeck[cardIndex += 1];
            scr = card.flipped ? card.faceImage : card.backImage;
            loadThenDisplayImage(  // long function call best split for readbility
                src, 
                left + (cardW + xSpacing) * column, 
                top + (cardH + ySpacing) * row
            );
        }
     }
}