requestAnimationFrame with this keyword
Solution 1
Now that ES6/2015 is here, if you are using a transpiler then an arrow function has lexical this
binding so instead of:
window.webkitRequestAnimationFrame(this.draw.bind(this));
you can do:
window.webkitRequestAnimationFrame(() => this.draw());
which is a bit cleaner.
I've used this effectively with Typescript transpiling to ES5.
Solution 2
I can't guarantee that this is a good idea and that I'm right, but running .bind on every requestAnimationFrame means creating a new function on every iteration. It just doesn't sound right to me.
That's why in my project I cached the bound function to avoid the anti-pattern.
Simple example:
var Game = function () {
this.counter = 0;
this.loop = function () {
console.log(this.counter++);
requestAnimationFrame(this.loop);
}.bind(this);
this.loop();
}
var gameOne = new Game();
If you have a more complex project with prototype inheritance you can still create a cached function with "this" bound in object's constructor
var Game = function () {
this.counter = 0;
this.loopBound = this.loop.bind(this);
this.loopBound();
}
Game.prototype.loop = function () {
console.log(this.counter++);
requestAnimationFrame(this.loopBound);
}
var gameOne = new Game();
Thoughts? http://jsfiddle.net/3t9pboe8/ (look in the console)
Solution 3
Beside bind
method, and arrow function solution (offered by Jamaes World's answer), Another (rather old) work around could be:
var self = this
window.webkitRequestAnimationFrame(
function() {
self.draw()
}
);
Ryan
Updated on July 09, 2022Comments
-
Ryan almost 2 years
I'm using
webkitRequestAnimationFrame
but I'm having trouble using it inside of an object. If I pass thethis
keyword it will usewindow
and I can't find a way for it to use the specified object instead.Example:
Display.prototype.draw = function(){ this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height); //Animation stuff here. window.webkitRequestAnimationFrame(this.draw); };
I have also tried this but to no avail:
Display.prototype.draw = function(){ this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height); //Animation stuff here. var draw = this.draw; window.webkitRequestAnimationFrame(draw); };
-
Ryan almost 8 yearsThis is what I now use.
-
insectean over 7 yearsI modified your fiddle to (I think) test the difference between caching the bound function and not. I was surprised to see nearly identical performance between the two (in Firefox 47, Chrome 52, and IE 11). It appears as though the function created by
bind
is cached by the browser, but I don't know what's actually going on. I even tried running each individually to avoid interference from the browser's optimization ofrequestAnimationFrame
. -
Pawel over 7 years@SeanH in this case "loop" is a really simple function consisting of a counter incrementation and one condition check. I wonder if using "bind" on every iteration of much more complex function would make a bigger difference. Probably the gain of this practice on requestAnimationFrame which never goes above 60fps is marginal.
-
jamess over 5 yearsI just used this technique, and I had to put an argument in the arrow function, corresponding to the timeStamp argument that the rAF() callback receives:
(t) => this.draw(t)