在渲染 JS 之前加载图像 canvas

Loading images before rendering JS canvas

我正在编写一个简单的游戏来学习 JS,我正在学习 HTML5,所以我需要在 canvas 上画东西。

代码如下:

 let paddle = new Paddle(GAME_WIDTH,GAME_HEIGHT);

 new InputHandler(paddle);

 let lastTime = 0;

 const ball = new Image();
 ball.src = 'assets/ball.png';

 function gameLoop(timeStamp){
   let dt = timeStamp - lastTime;
   lastTime = timeStamp;

   ctx.clearRect(0,0,600,600);
   paddle.update(dt);
   paddle.draw(ctx);

   ball.onload = () => {
    ctx.drawImage(ball,20,20);
  }

  window.requestAnimationFrame(gameLoop);
 }

gameLoop();

截图:没有球 before comment

现在我注释掉 clearRect():

after comment

你好球。

canvas底部还有一个桨,似乎不受clearRect()方法的影响。它工作得很好。我在这里错过了什么?

将图像的 onload 处理程序放在游戏循环中没有多大意义。这意味着游戏必须在设置图像的 onload 功能之前 运行 开始,从而导致相当混乱的情况。

正确的顺序是设置 onload 处理程序,然后是图像源,然后 await 所有图像 onload 在 运行 游戏循环之前触发.当你只有一张图片时,直接将主循环设置为 onload 非常容易,但对于具有多个资产的游戏,这很快就会变得尴尬。

这是一个简单的示例,说明您可以如何使用 Promise.all 加载许多游戏资产。您很可能希望将加载的图像解压缩为更具描述性的对象而不是数组,但这是一个开始。

const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 250;
const ctx = canvas.getContext("2d");

const assets = [
  "http://placekitten.com/120/100",
  "http://placekitten.com/120/120",
  "http://placekitten.com/120/140",
];
const assetsLoaded = assets.map(url =>
  new Promise(resolve => {
    const img = new Image();
    img.onerror = e => reject(`${url} failed to load`);
    img.onload = e => resolve(img);
    img.src = url;
  })
);

Promise
  .all(assetsLoaded)
  .then(images => {
    (function gameLoop() {
      requestAnimationFrame(gameLoop);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      images.forEach((e, i) =>
        ctx.drawImage(
          e, 
          i * 120, // x
          Math.sin(Date.now() * 0.005) * 20 + 40 // y
        )
      );
    })();
  })
  .catch(err => console.error(err))
;