Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas unable to find context when requestAnimationFrame is called - ES6 [duplicate]

I am experimenting with class based code. I create a class for the canvas and pass a component to it which is then rendered. The problem is that when the browser requests the animation frame the context is lost, and becomes undefined. I am looking for an explanation of why this is the case.

I am not concerned with best practice just with figuring out why the context is lost.

I have attached a link to the example on codepen.

http://codepen.io/BrianDGLS/pen/BoJorM?editors=001

Here is the JS code:

class Canvas {
  constructor() {
    this.canvas = document.getElementById('canvas');
    this.context = this.canvas.getContext('2d');
    this.width = this.canvas.width = window.innerHeight;
    this.height = this.canvas.height = window.innerWidth;
    this.components = [];
  }

  draw() {
    this.context.clearRect(0, 0, this.width, this.height);
    this.context.globalCompositeOperation = 'hard-light';

    this.components.map(_ => _.render());

    window.requestAnimationFrame(this.draw);
  }

  listeners() {
    window.addEventListener('resize', () => {
      this.width = this.canvas.width = window.innerHeight;
      this.height = this.canvas.height = window.innerWidth;
    }, false);
  }

  init() {
    this.listeners();
    this.draw();
  }
}

class Utils {
  randomNum(max, min) {
    return Math.floor(max * Math.random()) + min;
  }

  color(opacity) {
    return `hsla(${this.randomNum(360, 1)}, 70%, 60%, ${opacity})`;
  }
}

const utils = new Utils();
const _canvas = new Canvas();

class Stars {
  constructor(_) {
    this.total = _.total;
    this.spawn = [];
    this.z = 300;
    this.canvas = _.canvas;
    this.xw = this.canvas.width * this.z;
    this.xh = this.canvas.height * this.z;
  }

  create() {
    while (this.spawn.length < this.total) {
      this.spawn.push({
        pos: [this.xw * Math.random() - this.canvas.width / 2 * this.z, this.xh * Math.random() - this.canvas.height / 2 * this.z, this.z],
        vel: [0, 0, -2],
        r: utils.randomNum(500, 100),
        color: utils.color(1)
      });
    }
  }

  draw() {
    for (let i = 0; i < this.spawn.length; ++i) {
      let t = this.spawn[i];
      let x = t.pos[0] / t.pos[2];
      let y = t.pos[1] / t.pos[2];
      if (x < -this.canvas.width / 2 || x > this.canvas.width / 2 || y < -this.canvas.height / 2 || y > this.canvas.height / 2 || t.pos[2] < 0) {
        this.spawn.splice(i, 1);
        --i;
        continue;
      }
      this.canvas.context.beginPath();
      this.canvas.context.fillStyle = t.color;
      this.canvas.context.arc(x, y, t.r / t.pos[2], 0, Math.PI * 2, false);
      t.pos[0] += t.vel[0];
      t.pos[1] += t.vel[1];
      t.pos[2] += t.vel[2];
      this.canvas.context.fill();
    }
  }

  render() {
    this.create();
    this.canvas.context.save();
    this.canvas.context.translate(this.canvas.width / 2, this.canvas.height / 2);
    this.draw();
    this.canvas.context.restore();
  }
}

_canvas.components.push(new Stars({
  canvas: _canvas,
  total: 200
}));
like image 732
Brian Dgls Avatar asked Feb 09 '23 18:02

Brian Dgls


1 Answers

When you invoke draw() via window.requestAnimationFrame(this.draw) the scope in draw() is bound to window instead of Canvas. Try binding the scope explicitly like this:

window.requestAnimationFrame(this.draw.bind(this));
like image 157
Griffith Avatar answered Feb 11 '23 07:02

Griffith