我可以将数组值插入到画布的图像绘制函数中吗?

Can I plug in array values into a image drawing function of the canvas?

本文关键字:绘制 图像 函数 数组 插入 我可以      更新时间:2023-09-26

我想创建一行相同的图片,像这样:https://i.stack.imgur.com/XrWl7.jpg

每个草的图片是70x70像素。我想用一个for循环和一个存储25个值的数组来创建这个。这是代码:

var pic = new Image()
var picture = pic.src = "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSXEODf7WhGahJl_eRIh5Np063DS1MtQhjem1NDJLfdFB2am4YB";
var grassXs = [];
for (var i = 0; i < 25; i++) {
    grassXs.push(i*70);
}
for (var i = 0; i < grassXs.length;i++) {
    pic.addEventListener("load", function () {
        board.drawImage(pic,grassXs[i],430,70,70)
    }, false)
}

但由于某种原因,它根本不起作用。它不会创建一个单一的图像。是否可能是因为参数不允许从数组中"获取"值?还是我做错了什么?

您必须给您的草图像加载时间。

您可以使用图像对象上的onload回调来实现。

快速的例子:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var count=25;
var width=70;
var img=new Image();
img.onload=start;
img.src="https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSXEODf7WhGahJl_eRIh5Np063DS1MtQhjem1NDJLfdFB2am4YB";
function start(){
  canvas.width=width*count;
  canvas.height=img.height;
  for(var i=0;i<count;i++){
    ctx.drawImage(img,i*width,0,width,img.height);
  }
}
<canvas id="canvas" width=300 height=300></canvas>

[关于代码的附加说明]

例如,您的代码将按照以下顺序执行:
// a new image object is created
var pic = new Image()
// "pic" BEGINS to load, but is not yet fully loaded
//       and is not yet ready to draw
var picture = pic.src = "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSXEODf7WhGahJl_eRIh5Np063DS1MtQhjem1NDJLfdFB2am4YB";
// This code is immediately executed even though
// "pic" has not yet fully loaded
var grassXs = [];
// This entire loop is immediately executed even though
// "pic" has not yet fully loaded
for (var i = 0; i < 25; i++) {
    grassXs.push(i*70);
}
// This entire loop is immediately executed even though
// "pic" has not yet fully loaded
// Since "pic" is not fully loaded each "drawImage" below is
// drawing empty images onto the canvas
for (var i = 0; i < grassXs.length;i++) {
    // pic is being given 25 event handlers which all point
    // to the final value of "i". Since grassXs[25] is undefined 
    // all of the drawImages are ineffective
    pic.addEventListener("load", function () {
        board.drawImage(pic,grassXs[i],430,70,70)
    }, false)
}
// about this time (or later if the "pic" is a large image)
// "pic" is fully loaded and is now ready to drawImage with.
// Unfortunately, all the "drawImage"s have been executed with 
// invalid grassXs.

你犯了一个"闭包错误"。
综上所述,当你创建一个函数对象(在这个例子中是你的事件回调)时,它会"记住"它的上下文(在这个例子中是变量i),而不是直接记住它的值。问题是,在你的循环中,变量i发生了变化。当onload事件被调度时(在循环结束后),i的值为25。
因此,board.drawImage(pic,grassXs[25],430,70,70)(注意25)在加载图像时被调用24次。grassXs[25]undefined。这就是为什么没有显示。

为什么i是25而不是24 ?
因为for循环将执行您的代码(并增加i),直到条件i<24为假。当它停止时,i已经增加到25。
当你的回调被执行时,i仍然是25。

解决这个问题最简单的方法是纠正代码中的另一个缺陷。您正在做的是,您正在为图像对象上的相同事件注册24个EventListener s,并使用回调函数分别绘制一个图像。我还没有测试性能,但一个更简单的事情是注册一个EventListener与一个回调函数,绘制24张图片。
pic.addEventListener("load", function () {
  for (var i = 0; i < grassXs.length;i++) {
    board.drawImage(pic,grassXs[i],430,70,70);
  }
}, false)

此外,您的两个for循环具有完全相同的条件(grassXs.length在第一个循环后是25),因此您可以通过在第二个循环中乘以70来组合它们。以下是完整代码的更正版本:

var pic = new Image()
var picture = pic.src = "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSXEODf7WhGahJl_eRIh5Np063DS1MtQhjem1NDJLfdFB2am4YB";
var count = 25;
pic.addEventListener("load", function () {
  for (var i = 0; i < count;i++) {
    board.drawImage(pic,i*70,430,70,70);
  }
}, false)

如何处理闭包

如果你在将来遇到另一个关于闭包的问题,如果没有更简单的方法,这里是你可以如何纠正这个问题的方法。您应该阅读我上面发布的链接页面,但这里是您的代码示例。

您可以决定将代码放入外部函数中,并以i作为参数调用该函数:

function setListener(offset)
{
    pic.addEventListener("load", function () {
        board.drawImage(pic,offset,430,70,70);
    }, false) ;   
}
for(i = 0; i < 25; i++)
{
    setListener(i*70);
}

或者您可以使用自动执行的匿名函数,(在您的情况下,我会使用第一种方法,直到您对该语言更有经验。)

for(i = 0; i < 25; i++)
{
    (function(offset) {
        pic.addEventListener("load", function () {
            board.drawImage(pic, offset,430,70,70);
        }, false);
    })(i);
}

将函数用圆括号括起来,并在圆括号之间传递参数,就像调用普通函数一样。