Reputation: 8287
I found this and online and now trying to put it in TS.
Running the following throws Uncaught TypeError: Cannot set property 'toggle' of null
@Injectable()
export class HomeUtils {
private canvas: HTMLCanvasElement;
private context;
private toggle = true;
constructor() { }
public startNoise(canvas: HTMLCanvasElement) {
this.canvas = canvas;
this.context = canvas.getContext('2d');
this.resize();
this.loop();
}
private resize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
private loop() {
this.toggle = false;
if (this.toggle) {
requestAnimationFrame(this.loop);
return;
}
this.noise();
requestAnimationFrame(this.loop);
}
private noise() {
const w = this.context.canvas.width;
const h = this.context.canvas.height;
const idata = this.context.createImageData(w, h);
const buffer32 = new Uint32Array(idata.data.buffer);
const len = buffer32.length;
let i = 0;
for (; i < len;) {
buffer32[i++] = ((255 * Math.random()) | 0) << 24;
}
this.context.putImageData(idata, 0, 0);
}
}
I'm lost.
Upvotes: 29
Views: 25846
Reputation: 250366
Methods do not capture this
and are dependent on the caller to call them with the correct this
. So for example:
this.loop() // ok
let fn = this.loop;
fn(); // Incorect this
fn.apply(undefined) // Undefined this
Since you pass loop
to another function requestAnimationFrame
you need to ensure that this
is captured from the declaration context and not decided by requestAnimationFrame
:
You can either pass an arrow function to requestAnimationFrame
private loop() {
this.toggle = false;
if (this.toggle) {
requestAnimationFrame(() => this.loop());
return;
}
this.noise();
requestAnimationFrame(() => this.loop());
}
Or you can make loop an arrow function not a method:
private loop = () => {
this.toggle = false;
if (this.toggle) {
requestAnimationFrame(this.loop);
return;
}
this.noise();
requestAnimationFrame(this.loop);
}
The second approach has the advantage of not creating a new function instance on each call to requestAnimationFrame
, since this will be called a lot, you might want to go with the second version to minimize memory allocations.
Upvotes: 42
Reputation: 18312
It's the call to requestAnimationFrame
. You are passing a function not bound to a context, and so, inside that call to loop
there is no this
.
Change the call to:
requestAnimationFrame(() => this.loop());
Arrow functions, contrarily to normal functions, are bound to this
.
Upvotes: 9