HTML5 canvas 图像未使用 drawImage() 加载

HTML5 canvas images are not loading using drawImage()

我正在关注 freeCodeCamp 的飞鸟游戏教程 https://www.youtube.com/watch?v=pufKO5EG8nc

但我正在使用 class 来了解更多信息。

问题是,图像无法加载,而在视频中却可以正常加载。

我四处搜索并了解了 onload 事件,并在那里实现了它,但仍然没有任何反应,控制台中也没有错误消息。

即使没有 onload 事件,为什么视频的图像加载了,而我的却没有? >:(

我的HTML代码:

<canvas id="canvas" width="512" height="512">
</canvas>

我的JS代码:

const canvas = document.querySelector("#canvas");
class Game {
    constructor(c) {
        this.c = c; //canvas
        this.ctx = c.getContext("2d"); // context
        this.sprites = 5;
        this.loadedSprites = 0;

        this.player = new Image(); // define images
        this.bg = new Image();
        this.fg = new Image();
        this.north = new Image();
        this.south = new Image();

        this.pX = 10; // player starting location
        this.pY = 150;

        // set sprite locations and load.
        this.player.src = "images/crab.png";
        this.player.onload = this.draw;
        this.bg.src = "images/bg.png";
        this.bg.onload = this.draw;
        this.fg.src = "images/fg.png";
        this.fg.onload = this.draw;
        this.north.src = "images/obstacle.png";
        this.gap = 80;
        this.constant = this.north.height + this.gap;
        this.north.onload = this.draw;
        this.south.src = "images/obstacle.png";
        this.south.onload = this.draw;
    }

    //draw images
    draw() {
        this.loadedSprites += 1;
        if (this.loadedSprites >= this.sprites) {
            this.ctx.drawImage(this.bg, 0, 0);
            this.ctx.drawImage(this.north, 100, 0);
            this.ctx.drawImage(this.south, 0, 0 + this.constant);
            this.ctx.drawImage(this.fg, 0, this.c.height - this.fg.height);
            this.ctx.drawImage(this.player, this.pX, this.pY);
        }
    }
}
let game = new Game(canvas);

您的问题与 scope 有关 - 它指的是变量、对象和函数的可访问性 - 以及 this 关键字取决于实际范围。

为了更好地理解让我们看下面的例子:

class Game {
  constructor() {
    console.log("Game constructor:", this);
    this.player = new Image();
    this.player.onload = this.draw;
    this.player.src = "https://picsum.photos/200/300";
  }

  draw() {
    console.log("draw:", this);
  }
}
let game = new Game();

如果单击 'Run code snippet',您应该会在控制台中看到以下输出:


Game constructor: {}
draw: <img src="https://picsum.photos/200/300"></img>

如您所见,draw() 中对 this 的调用不再指向同一事物。在第一种情况下,它是 Game 的对象实例(由大括号提示),在第二种情况下,它是 HTMLImageElement 的实例。

所以你的代码失败了,因为:

this.loadedSprites += 1;
if (this.loadedSprites >= this.sprites) {

不要引用游戏 class 的私有变量 loadedSpritessprites - 事实上它根本没有引用任何东西,因为它在因此绘图函数 loadedSprites 永远不会增加。

一种解决方法是使用新的 属性 将游戏 class 的上下文传递给每个图像实例,例如.reference.

this.player = new Image();
this.player.reference = this;
this.player.onload = this.draw;

并在 draw() 中使用它,例如:

draw() {
     this.reference.loadedSprites += 1;
}

这是一个基于您的代码的完整示例:

const canvas = document.querySelector("#canvas");
class Game {
  constructor(c) {
    this.c = c; //canvas
    this.ctx = c.getContext("2d"); // context
    this.sprites = 5;
    this.loadedSprites = 0;

    this.player = new Image(); // define images
    this.player.reference = this;
    this.bg = new Image();
    this.bg.reference = this;
    this.fg = new Image();
    this.fg.reference = this;
    this.north = new Image();
    this.north.reference = this;
    this.south = new Image();
    this.south.reference = this;

    this.pX = 10; // player starting location
    this.pY = 150;

    // set sprite locations and load.

    this.player.onload = this.draw;
    this.player.src = "https://picsum.photos/id/237/20/20";

    this.bg.onload = this.draw;
    this.bg.src = "https://picsum.photos/id/22/20/20";

    this.fg.onload = this.draw;
    this.fg.src = "https://picsum.photos/id/30/20/20";

    this.gap = 80;
    this.constant = this.north.height + this.gap;

    this.north.onload = this.draw;
    this.north.src = "https://picsum.photos/id/37/20/20";

    this.south.onload = this.draw;
    this.south.src = "https://picsum.photos/id/137/20/20";
  }

  //draw images
  draw() {
    let ref = this.reference;
    ref.loadedSprites += 1;
    if (ref.loadedSprites >= ref.sprites) {
      ref.ctx.drawImage(ref.bg, 0, 0);
      ref.ctx.drawImage(ref.north, 100, 0);
      ref.ctx.drawImage(ref.south, 0, 0 + ref.constant);
      ref.ctx.drawImage(ref.fg, 0, ref.c.height - ref.fg.height);
      ref.ctx.drawImage(ref.player, ref.pX, ref.pY);
    }
  }
}
let game = new Game(canvas);
<canvas id="canvas" width="512" height="512">
</canvas>