javascript, назначение requestAnimationFrame и cancelAnimationFrame свойствам объекта

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

Пока я изучал некоторые примеры кодов, я нашел requestAnimationFrame. Версия 1 вырезана из нее и работает нормально. После некоторого поиска я также нашел cancelAnimationFrame и хотел использовать их обоих. Поэтому я сделал фиктивную страницу для теста. Версия 2 вырезана из него.

Проблема в том, что он не зацикливается. Итак, у меня есть два вопроса.

  1. Нельзя ли использовать requestAnimationFrame таким образом? Если да, то почему именно так?

  2. Если это возможно, но я делаю это неправильно, как я могу этого добиться?


person Ian Kim    schedule 27.06.2017    source источник
comment
У вас есть синтаксическая ошибка в вашем модуле: ccl: ccl, myReq: 0; // Remove semicolon   -  person Serge K.    schedule 27.06.2017
comment
Он работает нормально, пока я не попытаюсь его скрутить. - тогда покажите нам, как вы пытались его скрутить, пожалуйста.   -  person Bergi    schedule 27.06.2017
comment
1) Можно отредактировать вопрос, чтобы сделать его лучше, но не превращайте его в совершенно другой вопрос, особенно если на него уже есть ответы. 2) Вы можете удалить свои собственные комментарии, нажав на (X), вы можете пометить мои как устаревшие, но это проще, когда я удаляю их сам.   -  person Bergi    schedule 29.06.2017
comment
@ Натан П. Не могли бы вы удалить свой комментарий, пожалуйста? Спасибо.   -  person Ian Kim    schedule 30.06.2017
comment
@Bergi Все готово :)   -  person Ian Kim    schedule 30.06.2017


Ответы (1)


Это дубликат (Uncaught TypeError: недопустимый вызов в Chrome). Но я все равно отвечу на свой вопрос более подробно, так как это может помочь другим понять этот вопрос по-другому.

  1. Нельзя ли таким образом использовать requestAnimationFrame? Если да, то почему именно так?

Это невозможно.

  1. Если это возможно, но я делаю это неправильно, как я могу этого добиться?

Эта проблема сама по себе может быть легко устранена с помощью методов call(), bind() и apply().

//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]);

Обратите внимание на сходство в том, что все 3 метода каким-то образом имеют дополнительный параметр «окно». У всех одна и та же причина: requestAnimationFrame — это метод объекта window, который требует контекста window.

aniFram является объектом. У него есть метод req, который ссылается на window.requestAnimationFrame. aniFram.req(main) вызывает window.requestAnimationFrame в контексте aniFram, а не window. Вот почему это не работает. Рассмотрим другой пример кода:

Пример кода

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'

Это точно такая же ситуация, как и ваш код версии 2. Вы вызываете obj2.hitman(), который ссылается на obj1.hitman, ожидая изменить значение obj1.target, но он ничего не делает. Потому что obj1.hitman выполняет оператор this.target = 'RIP'. Поскольку он выполняется в контексте obj2, этот оператор становится obj2.target = 'RIP'. В obj2 нет свойства target.

звоните, обращайтесь, связывайтесь

Вот тут-то и вступают в дело эти методы. Без них Javascript Engine автоматически определяет контекст (текущий объект, т.е. aniFram, obj2). Прикрепив эти методы к вашему коду, теперь вы можете решить, в каком контексте он будет выполняться (например, в окне, obj1).


Это также называется функцией псевдонима. (Если в Javascript есть первоклассные функции, почему это работа?)

person Ian Kim    schedule 30.06.2017