Search code examples
javascriptrequestanimationframe

Using requestAnimationFrame in a class


requestAnimationFrame does not return a regular this when I use it in a class.

I've been looking all posts about the subject (tried to encapsulate the step() function as an anonymous function into animate...), but I still have the same issue.

class Sprite {

  constructor(canvas, imageSrc) {
    this.canvas = canvas;
    this.img = new Image();
    this.img.src = imageSrc;
    this.height = 18;
    this.width = 16;
    this.scale = 4;
    this.scaledWidth = this.scale * this.width;
    this.scaledHeight = this.scale * this.height;
    this.cycleLoop = [0];
    this.currentLoopIndex = 0;

    this.img.onload = () => {
      this.init();
    }
  }

  init() {
    this.ctx = this.canvas.getContext('2d');
    this.ctx.webkitImageSmoothingEnabled = false;
    this.ctx.mozImageSmoothingEnabled = false;
    this.ctx.imageSmoothingEnabled = false;
  }

  drawFrame(frameX, frameY, canvasX, canvasY) {
    this.ctx.drawImage(
      this.img,
      frameX * this.width,
      frameY * this.height,
      this.width,
      this.height,
      canvasX,
      canvasY,
      this.scaledWidth,
      this.scaledHeight
    );
  }

  animate() {
    requestAnimationFrame(this.step());
  }

  step() {
    console.log(this);
    console.log(this.ctx);
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

    this.drawFrame(this.cycleLoop[this.currentLoopIndex], 0, 0, 0);
    this.currentLoopIndex++;


    if (this.currentLoopIndex >= this.cycleLoop.length) {
      this.currentLoopIndex = 0;
    }
    requestAnimationFrame(this.step());
  }
}

In step(), the first console.log(this) shows a Character object with ctx attributes. The second console.log(this.ctx) is undefined for some reasons.

So I get a:

Uncaught TypeError: Cannot read property 'clearRect' of undefined
    at Character.step (sprite.js:49)

Solution

  • this.canvas = canvas;
    

    is defined inside the construtor of your Sprite class.

    this.ctx = this.canvas.getContext('2d');
    

    however is defined inside the callback function for the Image's onload event. So my best guess is the time you call the animate() method on a Sprite instance the onload event didn't fire yet thus the context isn't defined.