javascript, assigning requestAnimationFrame & cancelAnimationFrame to properties of an object

225 Views Asked by At
//
// version 1
//
var requestAnimFram = (function(){
    return window.requestAnimationFrame    ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame    ||
        window.oRequestAnimationFrame      ||
        window.msRequsetAnimationFrame     ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        }
})();

//version 1 usage
function main(){
    //main loop
    ...
    requestAnimFram(main);
}
main();

//
// version 2
//
var animFram = {
    req: window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        },
    ccl: window.cancelAnimationFrame ||
        window.mozCancelAnimationFrame,
    myReq: 0
};

//version 2 usage
function main(){
    ...
    aniFram.myReq = aniFram.req(main);
}
main();

function stop(){
    aniFram.ccl(myReq);
}

While I was exploring some example codes, I found requestAnimationFrame. version 1 is carved from that and it works fine. After seaching for a while, I found cancelAnimationFrame as well and wanted to use both of them. So I made a dummy page for test. The version 2 is carved from it.

The problem is that, it doesn't loop. So, I have two questions.

  1. Is it impossible to use requestAnimationFrame in this way? If so, why exactly so?

  2. If it's possible-but I'm doing it in wrong way, how can I acheive this?

1

There are 1 best solutions below

0
Ian Kim On BEST ANSWER

This is a duplicate("Uncaught TypeError: Illegal invocation" in Chrome). But I'm still gonna answer my own question with more detail, as such could help others to understand this matter in a different way.

  1. Is it impossible to use requestAnimationFrame in this way? If so, why exactly so?

It is impossible.

  1. If it's possible-but I'm doing it in wrong way, how can I acheive this?

This problem, by itself, can be easily fixed by using call() and bind() and apply() methods.

//Using call()
//Attach call method during invocation
aniFram.req.call(window, main);

//Using bind()
//Attach bind method during the object initialization
aniFram = {
    req: requestAnimationFrame.bind(window)
    ...
}
aniFram.req(main);

//Using apply()
//Attach apply method during invocation
aniFram.req.apply(window, [main]);

Notice the similarity in here that all 3 methods somehow have an additional parameter 'window'. They all have a same reason for it: requestAnimationFrame is a method of window object that requires the context of window.

aniFram is an object. It has a method req which references the window.requestAnimationFrame. aniFram.req(main) invokes the window.requestAnimationFrame in the context of the aniFram, not window. That's why it doesn't work. Let's consider another example code:

Example Code

var obj1 = {
    target: 'Jake',
    hitman: function(){
        this.target = 'RIP';
    }
};
var obj2 = {
    //assigns obj1.hitman to obj2.hitman
    hitman: obj1.hitman
};  
obj2.hitman();
console.log(obj1.target); //outputs 'Jake'

/////////////////////////////////////////
//call() method
obj2.hitman.call(obj1);
console.log(obj1.target); //outputs 'RIP'

//apply() method
obj2.hitman.apply(obj1);
console.log(obj1.target); //outputs 'RIP'

//bind() method
var obj2 = {
    hitman: obj1.hitman.bind(obj1)
};
obj2.hitman();
console.log(obj1.target); //outputs 'RIP'

This is exactly same situation as your code, version 2. You invoke obj2.hitman() that references obj1.hitman expecting to change the value of obj1.target, but it does nothing. Because what obj1.hitman does is executing a statement this.target = 'RIP'. Since it is executed in the context of obj2, this statement becomes obj2.target = 'RIP'. There is no target property in obj2.

call, apply, bind

That's where these methods kick in. Without them, Javascript Engine determines context automatically (current object. ie. aniFram, obj2). By attaching these methods to your code, now you can decide in which context it will be executed (ie. window, obj1).


This is also called as an Alias Function. (If Javascript has first-class functions, why doesn't this work?)