requestAnimationFrame with this keyword

27,899

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()
    }
);
Share:
27,899
Ryan
Author by

Ryan

Updated on July 09, 2022

Comments

  • Ryan
    Ryan almost 2 years

    I'm using webkitRequestAnimationFrame but I'm having trouble using it inside of an object. If I pass the this keyword it will use window 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
    Ryan almost 8 years
    This is what I now use.
  • insectean
    insectean over 7 years
    I 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 of requestAnimationFrame.
  • Pawel
    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
    jamess over 5 years
    I 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)