-1
//
// 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?

Ian Kim
  • 1
  • 3
  • You have a syntax error in your module : `ccl: ccl, myReq: 0; // Remove semicolon` – Serge K. Jun 27 '17 at 08:26
  • "*It works fine unless I try to twist it.*" - then show us how you tried to twist it, please. – Bergi Jun 27 '17 at 09:44
  • 1) It's fine to edit a question to make it better, but make sure not to change it into a completely different question especially if there already are answers. 2) You can delete your own comments by clicking on the (X), you could flag mine as "obsolete" but it's easier when I remove them myself. – Bergi Jun 29 '17 at 10:50
  • @Nathan P. Would you delete your comment, please? Thanks. – Ian Kim Jun 30 '17 at 11:21
  • @Bergi All is done :) – Ian Kim Jun 30 '17 at 11:21

1 Answers1

0

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?)

Ian Kim
  • 1
  • 3