将图像加载到多个画布时出现问题
我有一个模拟平铺图像的画布元素表。我只需要填充当前在视口中可见的那些图块。这是我使用的代码
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");
}
}
};
,但问题是仅加载该范围中的最后一个图块。同样的事情,但以这种方式实现工作正常:
$("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,并且对于两种方法,正确选择了要渲染的图块,并且从服务器下载了实际的图块图像,但首先方法仅显示最后一个图块,而对于第二种方法,所有图块都正确渲染。
预先感谢您的任何建议。
I have a table of canvas elements that emulates tiled image. I need to populate only those tiles that are currently visible in the viewport. Here's the code I use
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");
}
}
};
But the problem is that only the last tile from the range is loaded. The same thing but implemented in this way is working fine:
$("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");
}
});
Difference in how the visible range is calculated shouldn't matter because I checked in Firebug and for both approaches tiles to render are selected correctly and actual tile images are downloaded from server but for first approach only last tile is displayed, while for second one all the tiles are rendered correctly.
Thanks in advance for any suggestions.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
差异的原因主要在于:
它所做的是创建一个闭包并将其分配给图像的
load
事件。闭包关闭了context
和image
变量(以及其他几个变量)。这里的关键是它对这些变量有一个持久引用,而不是创建函数时它们的值的副本。由于上面的代码处于循环中,因此创建的所有函数(闭包)都将引用相同的context
和image
- 最后创建的函数环形。在
each
版本中不会发生这种情况,因为在该版本中,闭包会关闭一个不会更改的变量,即 onecontext
以及通过调用您传递给each
的迭代器函数创建的image
。有关闭包的更多信息,请参见:闭包并不复杂
也许稍微偏离主题,也许不是:如果您的
var
位于不同的位置,这可能会更清楚。var
有时会被误解。特别是,它与其他一些语言(例如 C、C++、Java 和 C#)中的变量声明不同,部分原因是 JavaScript 没有块级作用域(只有函数级作用域)和全球范围)。这就是 JavaScript 解释器如何看待您的第一个版本:
请注意,所有
var
语句都已移至顶部,因为这就是它们的处理方式。无论var
出现在函数中的哪个位置,它都会从函数的最开始处生效,并且只发生一次。如果var
有一个初始值设定项,那么它将成为var
所在位置的赋值。所以var x = 2;
实际上是两个完全独立的事情,由解释器在不同的时间处理:var x
,它在其他任何事情发生之前进入函数时发生,以及x = 2;
,它出现在函数代码的逐步执行中。(另外,题外话:在
for
循环的结束}
之后不需要;
;但是您确实需要在分配给onload
处理程序。我也将这两个 mods 都设置为 aboev。)以解释器看到的方式编写代码,现在(无论如何对我来说)更清楚了每个您分配给
onload
的闭包将引用相同context
和image
变量,因此它们'大家都会看到最后的。如果您想使用非
each
版本,只需稍微更改一下即可,以便onload
闭包在其自己的context
副本上关闭和图像
。看起来像这样:现在由
createOnloadHandler
函数创建的闭包在ct
和img
上关闭,而不是在context
上关闭图像
。每个闭包都有自己的副本(传递到创建该闭包的 createOnloadHandler 调用的副本)。The reason for the difference is primarily here:
What that does is create a closure and assign it to the
load
event of the image. The closure closes over thecontext
andimage
variables (and several others). The key thing here is that it has an enduring reference to those variables, not a copy of their values when the function was created. Since the above code is in a loop, all of the functions (closures) that get created will refer to the samecontext
andimage
— the last ones created by the loop.It doesn't happen in the
each
version because in that version, the closure closes over a variable that doesn't change, the onecontext
andimage
created by the call to the iterator function you're passing intoeach
.More about closures here: Closures are not complicated
Perhaps slightly off-topic, perhaps not: This may have been more clear if your
var
s were in a different place.var
is sometimes misunderstood. In particular, it's not the same as variable declarations in some other languages (C, C++, Java, and C# for instance), partially because JavaScript doesn't have block-level scope (only function-level scope and the global scope).This is how the JavaScript interpreter sees your first version:
Note that all the
var
statements have moved to the top, because that's how they'll be treated. Regardless of wherevar
appears in a function, it takes effect from the very beginning of the function and only happens once. If thevar
has an initializer, that becomes an assignment where thevar
is. Sovar x = 2;
is really two completely separate things that are handled at separate times by the interpreter:var x
, which happens upon entry to the function before anything else happens, andx = 2;
, which happens where it appears in the step-by-step execution of the function's code.(Also, off-topic: There's no need for a
;
after the closing}
of afor
loop; but you do want one after the assignment to theonload
handler. I've made both of those mods aboev as well.)Writing the code the way the interpreter will see it, now (to me, anyway) it's clearer that each of the closures you're assigning to
onload
will be referring to the samecontext
andimage
variables, and so they'll all see the last ones.If you want to use the non-
each
version, you just have to change it slightly so theonload
closures close over their own copy ofcontext
andimage
. That looks like this:Now the closure created by the
createOnloadHandler
function closes overct
andimg
, notcontext
andimage
. Each closure gets its own copy (the one passed into thecreateOnloadHandler
call that created that closure).