如何将参数传递到图像加载事件中?

28

当我设置图像对象的src属性时,它会触发一个onload函数。我该如何向其中添加参数?

x = 1;
y = 2;
imageObj = new Image();
imageObj.src = ".....";
imageObj.onload = function() {
    context.drawImage(imageObj, x, y);
};
x = 3;
y = 4;

在这里,我想使用在设置图像的src时设置的x和y值(即1和2)。在上面的代码中,当onload函数完成时,x和y可能是3和4。

有没有一种方法可以将值传递给onload函数,或者它会自动使用1和2?

谢谢


2
另一种方式:imageObj.coords={x:x,y:y}; ... drawImage(imageObj, imageObj.coords.x, imageObj.coords.y); - dandavis
1
计算机科学技术相关内容:上述问题在于onload函数引用了全局变量x和y...并且在执行时将使用当前值。为了正确使用闭包——并在执行时使用从创建时间(闭包)开始“保存”的变量值——您需要使用var声明在闭包内部给它们一个范围。(如果变量是函数的形式参数,则会隐式发生这种情况,这就是为什么基于闭包的答案“有效”的原因。) - Dan H
4个回答

46

所有其他答案都是“创建闭包”的某个版本。好的,这行得通。我认为闭包很酷,支持它们的语言也很酷...

但是:在我看来,有一种更简洁的方法可以实现这一点。只需使用图像对象存储所需内容,并通过"this"在加载处理程序中访问它即可:

imageObj = new Image();
imageObj.x = 1;
imageObj.y = 2;
imageObj.onload = function() {
    context.drawImage(this, this.x, this.y);
};
imageObj.src = ".....";

这是一种非常通用的技术,我经常在DOM中的许多对象中使用它。(特别是当我有四个按钮并希望它们都共享一个“onclick”处理程序时,我会使处理程序从按钮中提取一些自定义数据来执行该按钮的特定操作。)

一个警告:你必须小心,不要使用对象具有特殊含义或用途的属性。(例如:你不能为任何老的自定义用途使用imageObj.src;你必须将其留给源URL。)但是,在一般情况下,你怎么知道一个特定对象如何使用其所有属性呢?严格来说,你无法知道。因此,为了尽可能地使这种方法安全:

  • 将所有自定义数据打包到单个对象中
  • 将该对象分配给一个不寻常或不太可能被对象本身使用的属性。

在这方面,“x”和“y”的使用有些冒险,因为某些浏览器中的JavaScript实现可能在处理图像对象时使用这些属性。但这可能是安全的:

imageObj = new Image();
imageObj.myCustomData = {x: 1, y: 2};
imageObj.onload = function() {
    context.drawImage(this, this.myCustomData.x, this.myCustomData.y);
};
imageObj.src = ".....";

这种方法的另一个优点是:如果您正在创建大量给定对象,它可以节省很多内存,因为您现在可以共享一个onload处理程序的单个实例。考虑一下,使用闭包:

// closure based solution -- creates 1000 anonymous functions for "onload"
for (var i=0; i<1000; i++) {
   var imageObj = new Image();
   var x = i*20;
   var y = i*10;
   imageObj.onload = function() {
       context.drawImage(imageObj, x, y);
   };
   imageObj.src = ".....";
}

相较于共享加载函数,将您的自定义数据藏在图像对象中:

// custom data in the object -- creates A SINGLE "onload" function
function myImageOnload () {
   context.drawImage(this, this.myCustomData.x, this.myCustomData.y);
}
for (var i=0; i<1000; i++) {
   imageObj = new Image();
   imageObj.myCustomData = {x: i*20, y: i*10};
   imageObj.onload = myImageOnload;
   imageObj.src = ".....";
}

由于不需要创建所有这些匿名函数,因此可以节省很多内存并且可能运行速度更快。 (在这个示例中,onload函数是一行代码....但我有100行的onload函数,1000个它们肯定被认为是没有好理由浪费很多内存。)

更新:请参见使用"data-*"属性进行标准(和“标准批准”)方法,代替我临时建议使用myCustomData


4
哇,太神奇了,我多少年都没想过这个解决方法。太棒了,Dan,谢谢你。 - Tom Smykowski
酷!我使用imageObj.this = this代替了imageObj.myCustomData,然后使用this.this进行调用。这比硬编码字段更容易。 - Jason
实际上在 onload 函数中放置 ths = this.this;,这样就可以使用 ths.x 调用。 - Jason
imageObj.x = 1; 出现错误,因为 x 是只读的。 - Markus
@Markus:我提议的访问方式可能在后续版本的JavaScript/ECMAScript中受到限制。尝试使用“dataset”属性:https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset - Dan H

12

创建一个私有作用域的闭包,用于存储 x 和 y 值:

imageObj.onload = (function(x,y){
        return function() {
            context.drawImage(imageObj, x, y);
    };
})(x,y);

10
创建一个处理它的小函数。本地变量将保持正确的作用域。
function loadImage( src, x, y) {

    var imageObj = new Image();
    imageObj.src = src;
    imageObj.onload = function() {
        context.drawImage(imageObj, x, y);
    };

}

var x = 1,
    y = 2;

loadImage("foo.png", x, y);
x = 3;
y = 4;

我先尝试了你下面的内联答案(https://dev59.com/6mMm5IYBdhLWcg3wfu82#17578340),但它仍然表现得好像没有捕获到范围内的局部变量,但这种方法解决了这个问题。(它是我内联函数代码的复制粘贴)。我很想知道为什么这样做有效,但如果你能分享一下匿名的原因就更好了。 - Fluffeh

6
您可以使用匿名函数。
x = 1;
y = 2;
(function(xValue, yValue){
    imageObj = new Image();
    imageObj.src = ".....";
    imageObj.onload = function() {
        context.drawImage(imageObj, xValue, yValue);
    };
})(x,y);
x = 3;
y = 4;  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接