【问题标题】:HTML canvas drawImage() method not loading tile map (grid of multiple images) on first loadHTML画布drawImage()方法在第一次加载时不加载平铺地图(多个图像的网格)
【发布时间】:2019-03-18 16:38:20
【问题描述】:

这是关于创建平铺地图(64x64 像素 PNG)。

出于演示目的,我简化了代码并将网格缩小为 3x3 字段。

问题本质上是当我绘制网格时图像仍在加载,因此第一次加载时画布不会显示。然而,在刷新时,网格加载正常,因为所有图像文件现在都被缓存了。

<body>
    <canvas id="canvas" width="200px" height="200px"></canvas>

    <script>
        var canvas = document.getElementById('canvas');
        var context = canvas.getContext('2d');

        // 3X3 GRID
        var mapArray=[
            ["tile1","tile3","tile1"],
            ["tile1","tile1","tile2"],
            ["tile3","tile1","tile2"]
        ];

        var tile1 = new Image();
        var tile2 = new Image();
        var tile3 = new Image();

        tile1.src='../images/test/tile1.png';
        tile2.src='../images/test/tile2.png';
        tile3.src='../images/test/tile3.png';

        var posX=0;
        var posY=0;

            for(var i=0; i < mapArray.length; i++) {
                for(var j=0; j < mapArray[i].length; j++) {

                    switch(mapArray[i][j]) {
                        case "tile1":
                            context.drawImage(tile1, posX, posY, 64, 64);
                            break;
                        case "tile2":
                            context.drawImage(tile2, posX, posY, 64, 64);
                            break;
                        case "tile3":
                            context.drawImage(tile3, posX, posY, 64, 64);
                            break;
                    }
                    posX+=64;
                }
                posX=0;
                posY+=64;
            }
        </script>
</body>

可以找到类似的问题:

Canvas image not displaying until second attempt

Why do my canvases only load on refresh?

在这种情况下,虽然我试图生成的不仅仅是一张图像,而是一张图像网格,而我试图解决这个问题的尝试似乎遇到了问题,因为根本没有加载任何内容:

<body>
    <canvas id="canvas" width="200px" height="200px"></canvas>

    <script>
        var canvas = document.getElementById('canvas');
        var context = canvas.getContext('2d');

        // 3X3 GRID
        var mapArray=[
            ["tile1","tile3","tile1"],
            ["tile1","tile1","tile2"],
            ["tile3","tile1","tile2"]
        ];

        var tile1 = new Image();
        var tile2 = new Image();
        var tile3 = new Image();

        var posX=0;
        var posY=0;

            for(var i=0; i < mapArray.length; i++) {
                for(var j=0; j < mapArray[i].length; j++) {

                    switch(mapArray[i][j]) {
                        case "tile1":
                            //use onload so drawing waits until image file is loaded
                            tile1.onload = function() {
                                //draw image
                                context.drawImage(tile1, posX, posY, 64, 64);
                            };
                            //load image file
                            tile1.src='../images/test/tile1.png';
                            break;
                        case "tile2":
                            tile2.onload = function() {
                                context.drawImage(tile2, posX, posY, 64, 64);
                            };
                            tile2.src='../images/test/tile2.png';
                            break;
                        case "tile3":
                            tile3.onload = function() {
                                context.drawImage(tile3, posX, posY, 64, 64);
                            tile3.src='../images/test/tile3.png';
                            break;
                    }
                    posX+=64;
                }
                posX=0;
                posY+=64;
            }
        </script>
</body>

如您所见,我的 3 个开关选项现在都在等待图像加载后再绘制。

我在某处读到它可能与 FOR 循环有关,我必须使用 IIFE,我在这里找到了更多信息: What is the (function() { } )() construct in JavaScript? 但是,我完全不熟悉这个术语/概念,也不知道我是否走在正确的轨道上。

我还尝试通过外部 js 加载它,然后在 body 标记中加载 onload="drawTileMap()" ,但这会引发更有趣的行为,因为它似乎真的加载随机图块而不是整个网格.

非常感谢任何帮助!

【问题讨论】:

    标签: javascript html canvas html5-canvas drawimage


    【解决方案1】:

    我不建议您沿着您在第二个代码块中尝试的路线。将资源加载例程和画布渲染功能分开(正如您在第一个代码示例中尝试做的那样),以便更好地维护。 JavaScript 是一种基于异步事件的语言,请充分利用它。

    我已将您的代码分成两个函数。一个检查是否所有图像都已加载,并在这种情况下调用第二个函数(作为参数传递给它)。

    当您熟悉并理解我的示例如何工作时,您应该阅读 JavaScript Promises (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise),因为它们是使用回调处理异步操作的更通用和更好的方法。

    示例代码如下:

    var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');
    
    // 3X3 GRID
    var mapArray = [
      ["tile1", "tile3", "tile1"],
      ["tile1", "tile1", "tile2"],
      ["tile3", "tile1", "tile2"]
    ];
    
    var tile1 = new Image();
    var tile2 = new Image();
    var tile3 = new Image();
    var nrOfImagesLoaded = 0;
    
    // Using base64 datauri's, so the script doesn't depend on external resources
    tile1.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAZElEQVR42u3QAQ0AAAjDMO5fNGCD0GUKmq7a/xYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgfgMNmH/BjHDEMQAAAABJRU5ErkJggg==';
    tile2.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAY0lEQVR42u3QAQ0AAAjDMO5fNGCD0M1BU70/LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMD9Bs3Jf8EvyPO9AAAAAElFTkSuQmCC';
    tile3.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAYklEQVR42u3QAQ0AAAgDoL9/aM3hhAg0mcljFSBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAu5bjgl/wXGcqdoAAAAASUVORK5CYII=';
    
    tile1.onload = function() {nrOfImagesLoaded++;}
    tile2.onload = function() {nrOfImagesLoaded++;}
    tile3.onload = function() {nrOfImagesLoaded++;}
    
    // Function that takes another function as an argument and calls it when 
    // all 3 images have been loaded
    function callWhenImagesLoaded(callback) {
      // Call the function when all 3 tiles have been loaded
      if(nrOfImagesLoaded == 3) {
        callback();
      // Otherwise poll again in 500 milliseconds to see if they are loaded
      } else {
        setTimeout(function() { callWhenImagesLoaded(callback) }, 500);
      }
    }
    
    // Call this, when all images are loaded
    function allImagesAreNowLoaded() {
      var posX = 0;
      var posY = 0;
    
      for (var i = 0; i < mapArray.length; i++) {
        for (var j = 0; j < mapArray[i].length; j++) {
    
          switch (mapArray[i][j]) {
            case "tile1":
              context.drawImage(tile1, posX, posY, 64, 64);
              break;
            case "tile2":
              context.drawImage(tile2, posX, posY, 64, 64);
              break;
            case "tile3":
              context.drawImage(tile3, posX, posY, 64, 64);
              break;
          }
          posX += 64;
        }
        posX = 0;
        posY += 64;
      }
    }
    
    // Tie it all togther - call allImagesAreNowLoaded when all 3 tiles have been loaded
    callWhenImagesLoaded(allImagesAreNowLoaded);
    &lt;canvas id="canvas" width="200px" height="200px"&gt;&lt;/canvas&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-19
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多