开发者

requestAnimationFrame with this keyword

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);
};
开发者_Go百科

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);
};


I'm trying to pass display.draw which is the function in which webkitRequestAnimationFram resides.

webkitRequestAnimationFrame will presumably call the function you pass in, something like this:

function webkitRequestAnimationFrame(callback)
{
    // stuff...
    callback();
    // other stuff...
}

At this point, you have dissociated (detached) the draw function from its invocation context. You need to bind the function (draw) to its context (the instance of Display).

You can use Function.bind, but this requires JavaScript 1.8 support (or just use the recommended patch).

Display.prototype.draw = function()
{
    // snip...

    window.webkitRequestAnimationFrame(this.draw.bind(this));
};


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.


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)


how about this:

Display.prototype.draw = function(){
  this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
  //Animation stuff here.

  window.webkitRequestAnimationFrame( $.proxy(function() {this.draw()}, this) );
};

...assuming you use jquery


you have not to use "this". Keep it simple.

var game = {
      canvas:null,
      context:null,

    init:function(){
            // init canvas, context, etc
    },      

    update:function(){
        //do something
        game.render();                        
        requestAnimationFrame(game.update, game.canvas);        
    },            
};


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()
    }
);


And also you might want to use requestAnimationFrame shim to make it work on all browsers https://github.com/kof/animation-frame

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜