【问题标题】:Why does my image preloading method work?为什么我的图像预加载方法有效?
【发布时间】:2014-11-04 16:50:25
【问题描述】:

我的问题

我们的目标是确保在新游戏开始之前所有图像都已完全加载。我的第二个解决方案(Fiddle B)比我的第一个解决方案(Fiddle A)更一致、更准确地实现了这个目标。 为什么?

JSFIDDLE A

JSFIDDLE B

方法

这是我的两种小提琴解决方案预加载图像的方法:

  • 游戏所需的所有资源都有一组绝对图像 URL
  • init() 函数有一个 for 循环,它为数组中的每个 URL 生成一个新的画布 Image()
  • 每个新创建的图像都被推入另一个数组
  • 所以,我们现在有了包含 URL 字符串的第一个数组,以及包含“HTMLImageElement”对象的第二个数组

Fiddle B 与 Fiddle A 的不同之处在于它利用了 '.onload' 事件以及一个计数器。这两个 fiddles 使用不同的方法来检查是否所有图像资源都已加载:

Fiddle A:比较两个数组的长度。如果他们匹配,开始游戏

for (var i = 0; i < allGameImageUrls.length; i++) {
        var img = new Image();
        img.src = allGameImageUrls[i];
        allGameImages.push(img);
        console.log(allGameImages.length);
        if (allGameImages.length >= allGameImageUrls.length) {
            setUpGame();
        } else {
            // images haven't loaded yet
            console.log('STILL LOADING');
            STAGE.fillText("Loading ...", 20, 400);
        }
    }

Fiddle B: 将第二个数组长度与计数器变量进行比较。每次图像的 '.onload' 事件完成时,此计数器都会增加 1。

for (var i = 0; i < allGameImageUrls.length; i++) {
    var img = new Image();
    img.onload = function () {
        assetCount++;
        console.log('assetCount = ' + assetCount);
        setUpGame();
    };
    img.src = allGameImageUrls[i];
    allGameImages.push(img);
}

我的问题已扩展

Fiddle A 经常(但并非总是)在完整的图像资产列表正确加载之前触发新游戏的开始,从而导致游戏错误。 Fiddle B 在允许新游戏开始之前始终加载所有图像资源。这可以从写入画布的“完全加载”消息中的两个小提琴中看到。

虽然我可以看到 Fiddle A 比 Fiddle B 工作得更好,但我不明白为什么它更优越。并非所有与加载 HTMLImageElements 相关的教程都使用“.onload”,不使用它的教程似乎完全足够了。

此外,我不明白为什么比较两个数组的长度不如将第二个数组长度与计数器比较准确。

我了解如何这两种解决方案不同,但我想知道为什么解决方案 B 比解决方案 A 更有效。

以往的研究

这里只是我之前为了回答我自己的问题所做的一些研究的例子。

  • An article on pre-loading images 没有任何对 .onload 事件的引用
  • this question 的公认解决方案不使用 .onload 事件,但它仍然有效
  • this other question 的公认解决方案与我的 Fiddle B 非常相似(尽管我后来才发现它)。但是,解决方案的解释并没有帮助我回答我自己的问题。

【问题讨论】:

  • 写得很好的问题,+1!
  • 你说有,但我看不到.. 小提琴 A 在哪里检查图像是否已加载? AFAIK 如果不使用加载事件,您无法判断是否下载了图像。
  • @Malk,小提琴 A 假设如果图像对象数组的长度与 URL 数组的长度匹配,则图像必须已加载。这样做的逻辑是有缺陷的,但当时我没有注意到,因为我使用此代码创建的游戏仍然有效(除了问题中描述的问题)。
  • 您现在看到,仅仅创建 HTMLImageElements 并不意味着图像已下载并准备好使用(这是异步发生的)。另外,我检查了你所有的链接,它们都使用了 onload。
  • @Malk 你是对的链接。我不知道我第一次是怎么错过的,我花了很长时间阅读这个。谢谢!

标签: javascript arrays html5-canvas theory


【解决方案1】:

您正在比较两种截然不同的方法:使用 for 的顺序和基于事件的方法。

图像下载是异步过程,因此当设置图像src 属性时,浏览器开始下载它。然而,它可能非常快,特别是如果图像已经被浏览器下载并在内部缓存(事实上,它非常快)。因此,当下一次迭代开始时,它已经可用(或者,至少,几乎所有它们在循环结束时都可用)。但是,如果您清除缓存或使用隐身模式并从远程位置(而不是您的本地服务器)下载它们,那么繁荣! - 循环结束,根本没有下载图像。

另一种方法稍微好一点,但游戏是为每个下载的图像设置的,这可能不是必需的。

考虑以下方法:

var length = allGameImageUrls.length,
    count = 0;

var i, img;

for (i = 0; i < length; i++) {
    img = new Image();
    img.onload = function () {
        count++;
        // count is increased on every callback
        // so if number of executed callbacks equals
        // the number of images then all the images
        // are downloaded
        if (count === length) {
            setUpGame();
        }
    };
    img.src = allGameImageUrls[i];
    allGameImages.push(img);
} 

唯一的缺点是如果其中一张图片不存在,游戏将永远不会开始,因此您需要通过超时来解决它。

【讨论】:

  • 谢谢!你的解释有帮助。也感谢您的提醒 - 我将使用不存在的图像对其进行测试并制定解决方案。
猜你喜欢
  • 2011-03-14
  • 2019-08-29
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-01
  • 1970-01-01
  • 2016-05-30
相关资源
最近更新 更多