gpt4 book ai didi

javascript - 将图像加载到多个 Canvas 时出现问题

转载 作者:行者123 更新时间:2023-11-28 00:26:51 24 4
gpt4 key购买 nike

我有一个模拟平铺图像的 Canvas 元素表。我只需要填充那些当前在视口(viewport)中可见的图 block 。这是我使用的代码

for (var ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
for (var iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
var canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "- disp");
var canvasDisp = canvasDispJq.get(0);
var image = new Image();
var context = canvasDisp.getContext("2d");
image.onload = function() {
context.drawImage(image, 0, 0);
}
image.src = self.baseURL + '/' + canvasDispJq.attr("id");
}
}
};

但问题是只加载范围内的最后一个图 block 。同样的事情,但以这种方式实现的工作正常:

$("table#canvasTable-" + frameResId + " canvas.display").each(function() {
var canvasDispJq = $(this);
if ((canvasDispJq.position().top + self.tileSize + imagePosition.y) > 0 &&
(canvasDispJq.position().left + self.tileSize + imagePosition.x) > 0 &&
(canvasDispJq.position().top + imagePosition.y) < self.containerHeight &&
(canvasDispJq.position().left + imagePosition.x) < self.containerWidth) {
var canvasDisp = canvasDispJq.get(0);
var image = new Image();
var context = canvasRaw.getContext("2d");
image.onload = function() {
context.drawImage(image, 0, 0);
}
image.src = self.baseURL + '/' + canvasDispJq.attr("id");
}
});

可见范围计算方式的差异无关紧要,因为我在 Firebug 中进行了检查,并且两种方法都正确选择了要渲染的图 block ,并且实际图 block 图像是从服务器下载的,但对于第一种方法,只显示最后一个图 block ,而对于第二个所有的瓷砖都被正确渲染。

提前感谢您的任何建议。

最佳答案

造成差异的原因主要在这里:

var image = new Image();
var context = canvasDisp.getContext("2d");
image.onload = function() {
context.drawImage(image, 0, 0);
}

它所做的是创建一个闭包并将其分配给图像的 load 事件。闭包关闭 contextimage 变量(以及其他几个)。这里的关键是它有一个对这些变量的持久引用,而不是创建函数时它们值的副本。由于上面的代码在一个循环中,所有创建的函数(闭包)都将引用相同的 contextimage - 循环创建的最后一个。

它不会发生在 each 版本中,因为在那个版本中,闭包关闭了一个不变的变量,one contextimage 通过调用传递给 each 的迭代器函数创建。

更多关于闭包的信息:Closures are not complicated

也许 稍微 偏离主题,也许不是:如果您的 var 位于不同的位置,这可能会更清楚。 var is sometimes misunderstood.特别是,它与某些其他语言(例如 C、C++、Java 和 C#)中的变量声明相同,部分原因是 JavaScript 没有 block 级范围(只有函数级范围和全局范围)。

这是 JavaScript 解释器如何看待您的第一个版本:

var ix, iy, canvasDispJq, canvasDisp, image, context;
for (ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
for (iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "- disp");
canvasDisp = canvasDispJq.get(0);
image = new Image();
context = canvasDisp.getContext("2d");
image.onload = function() {
context.drawImage(image, 0, 0);
};
image.src = self.baseURL + '/' + canvasDispJq.attr("id");
}
}
}

请注意,所有 var 语句都已移至顶部,因为这就是它们的处理方式。无论 var 出现在函数的哪个位置,它都从函数的最开始生效,并且只发生一次。如果 var 有一个初始值设定项,那么它将成为 var 所在的赋值。所以 var x = 2; 实际上是两个完全独立的东西,由解释器在不同的时间处理:var x,它在进入函数时发生,然后再发生任何其他事情, 和 x = 2;,它出现在函数代码的逐步执行中。

(此外,题外话:在 for 循环的结束 之后不需要 ;;但您确实想要一个在分配给 onload 处理程序之后。我也将这两个 mod 都制作成了 aboev。)

按照解释器看到的方式编写代码,现在(无论如何对我而言)更清楚的是,您分配给 onload 的每个闭包都将引用相同的 contextimage 变量,所以他们都会看到最后一个。

如果你想使用非each版本,你只需要稍微改变它,这样onload闭包就会关闭它们自己的context<副本image。看起来像这样:

var ix, iy, canvasDispJq, canvasDisp, image, context;
for (ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
for (iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "- disp");
canvasDisp = canvasDispJq.get(0);
image = new Image();
context = canvasDisp.getContext("2d");
image.onload = createOnloadHandler(context);
image.src = self.baseURL + '/' + canvasDispJq.attr("id");
}
}
}

function createOnloadHandler(ct, img) {

return function() {
context.drawImage(img, 0, 0);
};
}

现在由 createOnloadHandler 函数创建的闭包关闭 ctimg,而不是 context图像。每个闭包都有自己的副本(传递到创建该闭包的 createOnloadHandler 调用的副本)。

关于javascript - 将图像加载到多个 Canvas 时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5269194/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com