【问题标题】:HTML5 Canvas rendering oddlyHTML5 Canvas 渲染异常
【发布时间】:2012-04-19 18:51:42
【问题描述】:

我正在学习 HTML5(和 OO Javascript),并且正在尝试制作一个简单的游戏引擎,但渲染到画布的工作条件非常奇怪。 Here 是该页面的链接,这是我的 index.html:

<html>
<head>
    <script type="text/javascript">
        function load()
        {
            var game = new Game();
            game.start();   
        }
    </script>
    <title>Game</title>
    <style>
        canvas
        {
            outline:0;
            border:1px solid #000;
            margin-left: auto;
            margin-right: auto;
        }
    </style>
</head>
<body onload="load()">
    <canvas id='c'></canvas>

    <script src="classes/Tuple.js"></script>
    <script src="classes/Rect.js"></script>
    <script src="classes/Sprite.js"></script>
    <script src="classes/Game.js"></script>
</body>
</html>



首次加载时,它会在 Firefox 11.0 中给出 NS_ERROR_DOM_TYPE_MISMATCH_ERR 异常,在 Chrome 18.0 中会给出 TypeError 异常。让它发挥作用:

火狐11.0

  • 在地址栏中选择 URL 并手动回车或
  • 单击刷新按钮或
  • 按 F5。

Chrome 18.0

  • 在地址栏中选择 URL 并手动回车或
  • 单击刷新按钮或
  • 按 F5 或
  • 按 Shift/Ctrl + F5。

我的怀疑 - 只是猜测,但似乎一个或多个 .js 文件在第一个页面加载期间被调用时不可用/准备就绪。在 Firebug 中经历了几次之后,精灵图像似乎永远不会在第一页请求中加载。

也许一些 .js 文件仍在被下载,因为正在对 Game() 进行实例化,然后被缓存,并且只有在第二个页面加载时才可用?

有什么想法吗?非常感谢您的支持,谢谢!

【问题讨论】:

  • 嗯,我将 Game.js 设置为四个包含中的第一个,但我似乎得到了相同的行为(我强制刷新,所以我不会离开缓存副本)跨度>

标签: javascript html canvas rendering typeerror


【解决方案1】:

我在本地复制了您的文件并进行了一些测试。在我看来,不是你的 JavaScript 文件不可用,而是你的精灵。

在调用 Game.start 之前,您不会加载 PNG。当你这样做时:

roadSpritesheetImage.src = "assets/sprites/player/playerSpriteSheet.png";

这将开始异步下载图像。其余代码在图像完全加载之前执行,因此当您对图像进行切片时,就像 src 属性为空一样进行切片。

这就是为什么您能够点击刷新并且一切都按预期工作...图像被缓存。

你可以通过在画布之前添加一个图像标签来抢占你的精灵的加载:

<img src="assets/sprites/player/playerSpriteSheet.png" style="display:none;" />

这会起作用,因为在内容(包括图像)完全加载之前不会触发正文加载事件(我相信,我必须查一下)。然后,如果将此图像分配给img.src,您将引用缓存的图像。

或者,您可以将游戏的其余功能绑定到img.onload。例如:

self.loadSprites = function()
{
    var roadSpritesheetImage = new Image();     
    roadSpritesheetImage.onload = function() { 
        self.entities.push(new Sprite("testTile", 
            roadSpritesheetImage, 
            new Tuple(16, 16), 
            new Rect(0, 0, 32, 16)));
        self.run();
    };

    roadSpritesheetImage.src = "assets/sprites/player/playerSpriteSheet.png";
};

self.run = function() {
    var drawSuccessful = false;
    drawSuccessful = self.entities[0].draw(self.context);
    self.entities[0].nextFrame();

    //if we managed to draw the Sprite successfully without any raised exceptions
    if(drawSuccessful) {
        setTimeout(self.run, 300);
    } else { alert("stopping execution of the game."); }
};

self.start = function()
{
    self.loadSprites();
};

这样做会更干净一些,并且可以保证您的资源没有任何竞争条件。

我创建的一个 jQuery 插件也有类似的问题,该插件用于从源图像中绘制 N 拼图。我当时发现this MDN documentation 非常有用,尽管我最终使用了切片 div 而不是画布。

【讨论】:

  • 太好了,谢谢!在查看 Chrome 时间轴功能中的加载过程后,我开始怀疑同样的事情。但是我只是假设当您声明一个新的 Image() 对象时,它只会等到图像加载完毕后再继续。我现在看到情况并非如此。这是一个新帐户,所以我没有足够的声誉来支持您的帖子,但是当我这样做时,我会的。再次感谢,你让我免于困惑!
  • @AlexAlksne,您可以将其标记为答案。现在你需要考虑加载 2+ sprites 时,self.run() 不应该被调用,直到它们都被加载。
  • @AlexAlksne 没问题。在这种情况下,可能很难捕捉到加载时间……尤其是当您的精灵只有 4K 时。
  • @JimSchubert 那么假设您不应该依赖 JS 来按时加载资源是否安全?我是否应该制定一个政策,每当加载任何类型的资源(音频/视频/等)时,始终通过为该资源定义 onLoad 函数并在完成后调用 self.run 来暂停执行?
  • @AlexAlksne 您必须提供一些方法来确保您的资源在以代码形式访问之前可用。你可以做一些丑陋的加载链或将它们加载到 DOM 中。您可以使用AssetManager 来预加载所有内容,也可以使用CSS Animation 来指示加载状态...
猜你喜欢
  • 1970-01-01
  • 2012-03-23
  • 2013-09-22
  • 2014-03-13
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
相关资源
最近更新 更多