HTML метод drawImage () для холста не загружает карту листов (сетку из нескольких изображений) при первой загрузке - PullRequest
1 голос
/ 17 марта 2019

Речь идет о создании мозаичной карты (PNG размером 64x64 пикселей).

В демонстрационных целях я упростил свой код и уменьшил сетку до поля 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>

Можно найти похожие проблемы:

Изображение холста не отображается до второй попытки

Почему мои холсты загружаются только при обновлении?

В этом случае я пытаюсь создать не одно изображение, а сетку изображений и мою попыткурешение этой проблемы, похоже, наталкивается на проблемы, поскольку ничего не загружается вообще:

<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>

Как вы можете видеть, каждый из трех вариантов переключателя теперь ждет загрузки изображения перед его рисованием.

Я где-то читал, что это может быть связано с циклом FOR, и мне придется использовать IIFE, который я нашел здесь: Что такое конструкция (function () {}) () в JavaScript? Однако я совершенно незнаком с этим термином / концепцией, и при этом я не знаю, нахожусь ли я на правильном пути.

Я также пытался загрузить это через внешний js и затем onload = "drawTileMap ()" в теге body, однако это вызывает еще более забавное поведение, потому что тогда действительно кажется, что загружаются случайные плитки вместовся сетка.

Любая помощь высоко ценится!

1 Ответ

0 голосов
/ 18 марта 2019

Я бы не советовал идти по маршруту, который вы пытаетесь использовать во втором блоке кода.Храните подпрограммы загрузки ресурсов и функции визуализации холста отдельно (как вы пытались сделать в первом примере кода) для лучшего обслуживания.JavaScript - это язык, основанный на асинхронных событиях, используйте его в своих интересах.

Я разделил ваш код на две функции.Тот, который проверяет, все ли изображения загружены, и вызывает вторую функцию (переданную ему в качестве аргумента), когда это так.

Когда вам удобно и вы понимаете, как работает мой пример, вы должны прочитатьОбещания JavaScript (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);
<canvas id="canvas" width="200px" height="200px"></canvas>
...