将图像加载到多个画布时出现问题

发布于 2024-10-21 23:27:41 字数 1692 浏览 3 评论 0原文

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

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

墨洒年华 2024-10-28 23:27:41

差异的原因主要在于:

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

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

each 版本中不会发生这种情况,因为在该版本中,闭包会关闭一个不会更改的变量,即 one context以及通过调用您传递给 each 的迭代器函数创建的 image

有关闭包的更多信息,请参见:闭包并不复杂

也许稍微偏离主题,也许不是:如果您的var位于不同的位置,这可能会更清楚。 var 有时会被误解。特别是,它与其他一些语言(例如 C、C++、Java 和 C#)中的变量声明不同,部分原因是 JavaScript 没有块级作用域(只有函数级作用域)和全球范围)。

这就是 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 处理程序。我也将这两个 mods 都设置为 aboev。)以

解释器看到的方式编写代码,现在(无论如何对我来说)更清楚了每个您分配给 onload 的闭包将引用相同 contextimage 变量,因此它们'大家都会看到最后的。

如果您想使用非 each 版本,只需稍微更改一下即可,以便 onload 闭包在其自己的 context 副本上关闭和图像。看起来像这样:

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 调用的副本)。

The reason for the difference is primarily here:

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

What that does is create a closure and assign it to the load event of the image. The closure closes over the context and image 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 same context and image — 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 one context and image created by the call to the iterator function you're passing into each.

More about closures here: Closures are not complicated

Perhaps slightly off-topic, perhaps not: This may have been more clear if your vars 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:

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");
        }
    }
}

Note that all the var statements have moved to the top, because that's how they'll be treated. Regardless of where var appears in a function, it takes effect from the very beginning of the function and only happens once. If the var has an initializer, that becomes an assignment where the var is. So var 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, and x = 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 a for loop; but you do want one after the assignment to the onload 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 same context and image 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 the onload closures close over their own copy of context and image. That looks like this:

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

Now the closure created by the createOnloadHandler function closes over ct and img, not context and image. Each closure gets its own copy (the one passed into the createOnloadHandler call that created that closure).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文