在 Javascript 中调整重复图像的大小

Resize Repeated Image in Javascript

我的代码使用二维数组来知道在网格布局上显示什么图像。使用的图像是 repeated.The 图像是在 javascript 文件中用 var ground = new Image() 创建的。我正在尝试调整图像的大小,但我无法弄清楚。我尝试过使用各种组合,例如 ground.value.height="10px"ground.height="10px"ground.value.height="10"ground.value.height=10

更具体地说,我想要的是图像的大小 canvas 除以二维数组的相关维度。即网格中的图块大小可以是 10px x 10px,我希望图像大小相同。如果它按照我认为的方式运行,那么当图像重复时,每个图块都应该重复显示的图像。

var gameContext = null;
var gameMap = [
  [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
  [0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 1, 0, 0, 0, 1, 1, 0],
  [0, 1, 0, 1, 0, 1, 0, 0, 1, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];

var mapW = 10,
  mapH = 10;
var boxEdge = window.innerWidth / mapW / 2;
var tileW = boxEdge,
  tileH = boxEdge;
var currentSecond = 0,
  frameCount = 0,
  framesLastSecond = 0;
var ground = new Image();
ground.src = "https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/65614919-0734-4dc8-9460-7034fd979346/dbg8qqd-0fb0aced-d05c-4df6-a7c6-b8e04c184ac5.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzY1NjE0OTE5LTA3MzQtNGRjOC05NDYwLTcwMzRmZDk3OTM0NlwvZGJnOHFxZC0wZmIwYWNlZC1kMDVjLTRkZjYtYTdjNi1iOGUwNGMxODRhYzUucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.9IQbxC3HC3uuJLf8V9Ridq005b2_-4zFg6Cb9rJ2tbw";
var wall = new Image();
wall.src = "data:image/png;base64,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";
window.onload = function() {
  gameCanvas = document.getElementById("game");
  gameContext = gameCanvas.getContext("2d");
  gameCanvas.width = window.innerWidth / 2;
  gameCanvas.height = window.innerWidth / 2;
  /*gameContext = document.getElementById('game').getContext("2d");
  gameContext.font = "bold 10pt sans-serif";*/
  requestAnimationFrame(drawGame);
};

function drawGame() {
  var groundPattern = gameContext.createPattern(ground, "repeat");
  var wallPattern = gameContext.createPattern(wall, "repeat");
  if (gameContext == null) {
    return;
  }

  var sec = Math.floor(Date.now() / 1000);
  if (sec != currentSecond) {
    currentSecond = sec;
    framesLastSecond = frameCount;
    frameCount = 1;
  } else {
    frameCount++;
  }

  for (var y = 0; y < mapH; ++y) {
    for (var x = 0; x < mapW; ++x) {
      switch (gameMap[y][x]) {
        case 0:
          gameContext.fillStyle = wallPattern;
          break;
        default:
          gameContext.fillStyle = groundPattern;
          //gameContext.fillStyle = "#5aa457";
      }

      gameContext.fillRect(x * tileW, y * tileH, tileW, tileH);
    }
  }

  //gameContext.fillStyle = "#ff0000";
  //gameContext.fillText("FPS: " + framesLastSecond, 10, 20);

  requestAnimationFrame(drawGame);
}
<!DOCTYPE html>
<html>

<head>
  <link rel="stylesheet" href="style.css">
</head>

<body style="background-color:powderblue;">
  <script src="script.js"></script>
  <canvas id="game"></canvas>
</body>

</html>

您可以将图块绘制为缩放图像而不是图案:

ctx.drawImage(
  imgAsset.img,
  col * tile.width,
  row * tile.height,
  tile.width,
  tile.height
);

此示例使用 CanvasRenderingContext2D.drawImage() 的第二个构造函数:

void ctx.drawImage(image, dx, dy, dWidth, dHeight);

在下面的示例中,我添加了资产查找地图以提高效率。您可以添加额外的图像资源并相应地更新您的瓷砖地图。该代码仍然有效。

const assets = {
  images: {
    wall: {
      id: 0,
      url: 'https://cdn.discordapp.com/attachments/808770424535777302/850378944653164604/wall.png'
    },
    ground: {
      id: 1,
      url: 'https://cdn.discordapp.com/attachments/808770424535777302/850378948168384562/ground.png'
    },
    water: {
      id: 2,
      url: 'https://cdn.discordapp.com/attachments/808770424535777302/850398765726302298/water.png'
    }
  }
};
const imgKeyLookup = Object.entries(assets.images)
  .reduce((acc, [key, { id }]) => acc.set(id, key), new Map);
const getImageAsset = id => assets.images[imgKeyLookup.get(id)];

let currentSecond = 0, frameCount = 0, framesLastSecond = 0;

const tileMap = [
  [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
  [0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 1, 2, 2, 2, 1, 1, 0],
  [0, 1, 0, 1, 2, 2, 0, 0, 1, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];

const ctx = document.querySelector('#game').getContext('2d');
const grid = { rows: tileMap.length, cols: tileMap[0].length };
const boxEdge = window.innerWidth / grid.cols / 2;
const tile = { width: boxEdge, height: boxEdge };

const loadImage = (url) => new Promise((resolve, reject) => {
  const img = new Image();
  img.addEventListener('load', () => resolve(img));
  img.addEventListener('error', (err) => reject(err));
  img.src = url;
});

const main = () => {
  Object.assign(ctx.canvas, {
    width: window.innerWidth / 2,
    height: window.innerWidth / 2
  });
  requestAnimationFrame(drawGame);
};

function drawGame() {
  const sec = Math.floor(Date.now() / 1000);
  if (sec != currentSecond) {
    currentSecond = sec;
    framesLastSecond = frameCount;
    frameCount = 1;
  } else {
    frameCount++;
  }

  for (let row = 0; row < grid.rows; row++) {
    for (let col = 0; col < grid.cols; col++) {
      const imgAsset = getImageAsset(tileMap[row][col]);
      if (imgAsset) {
        ctx.drawImage(imgAsset.img,
          col * tile.width, row * tile.height,
          tile.width, tile.height);
      }
    }
  }
  
  requestAnimationFrame(drawGame);
};

const imageAssets = Object.values(assets.images);
Promise.all(imageAssets.map(({ url }) => url).map(loadImage))
  .then(imgArr => imgArr.forEach((img, index) => {
    Object.assign(imageAssets[index], { img });
  }))
  .then(main);
html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  background-color: powderblue;
  align-items: center;
  justify-content: center;
}
<canvas id="game"></canvas>


设置 Image 构造函数的 widthheight 不会调整您的图块图像的大小(如下所示)。

const assets = {
  images: {
    wall: {
      id: 0,
      url: 'https://cdn.discordapp.com/attachments/808770424535777302/850378944653164604/wall.png'
    },
    ground: {
      id: 1,
      url: 'https://cdn.discordapp.com/attachments/808770424535777302/850378948168384562/ground.png'
    }
  }
};
const imgKeyLookup = Object.entries(assets.images)
  .reduce((acc, [key, { id }]) => acc.set(id, key), new Map);
const getImageAsset = id => assets.images[imgKeyLookup.get(id)];

let currentSecond = 0, frameCount = 0, framesLastSecond = 0;

const tileMap = [
  [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
  [0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 1, 0, 0, 0, 1, 1, 0],
  [0, 1, 0, 1, 0, 1, 0, 0, 1, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];

const ctx = document.querySelector('#game').getContext('2d');
const grid = { rows: tileMap.length, cols: tileMap[0].length };
const boxEdge = window.innerWidth / grid.cols / 2;
const tile = { width: boxEdge, height: boxEdge };

const loadImage = (url) => new Promise((resolve, reject) => {
  const img = new Image(tile.width, tile.height);
  img.addEventListener('load', () => resolve(img));
  img.addEventListener('error', (err) => reject(err));
  img.src = url;
});

const main = () => {
  Object.entries(assets.images).forEach(([key, value]) =>
    Object.assign(value, {
      pattern: ctx.createPattern(value.img, 'repeat')
    }));
  Object.assign(ctx.canvas, {
    width: window.innerWidth / 2,
    height: window.innerWidth / 2
  });
  requestAnimationFrame(drawGame);
};

function drawGame() {
  const sec = Math.floor(Date.now() / 1000);
  if (sec != currentSecond) {
    currentSecond = sec;
    framesLastSecond = frameCount;
    frameCount = 1;
  } else {
    frameCount++;
  }

  for (let row = 0; row < grid.rows; row++) {
    for (let col = 0; col < grid.cols; col++) {
      const imgAsset = getImageAsset(tileMap[row][col]);
      if (imgAsset) {
        ctx.fillStyle = imgAsset.pattern;
        ctx.fillRect(col * tile.width, row * tile.height, tile.width, tile.height);
      }
    }
  }
  
  requestAnimationFrame(drawGame);
};

const imageAssets = Object.values(assets.images);
Promise.all(imageAssets.map(({ url }) => url).map(loadImage))
  .then(imgArr => imgArr.forEach((img, index) => {
    Object.assign(imageAssets[index], { img });
  }))
  .then(main);
html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  background-color: powderblue;
  align-items: center;
  justify-content: center;
}
<canvas id="game"></canvas>

图像的大小应该传递给构造函数。

如果你想要 10px10px 图片,你需要写:

var image = new Image(10, 10)

Based on the https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image