#javascript #object #animation #this
#javascript #объект #Анимация #это
Вопрос:
Я использую webkitRequestAnimationFrame
, но у меня возникают проблемы с его использованием внутри объекта. Если я передам this
ключевое слово, которое он будет использовать window
, и я не могу найти способ для него использовать указанный объект вместо этого.
Пример:
Display.prototype.draw = function(){
this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
//Animation stuff here.
window.webkitRequestAnimationFrame(this.draw);
};
Я также пробовал это, но безрезультатно:
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);
};
Ответ №1:
Я пытаюсь передать display.нарисуйте, какая функция находится в webkitRequestAnimationFrame.
webkitRequestAnimationFrame
предположительно вызовет переданную вами функцию, что-то вроде этого:
function webkitRequestAnimationFrame(callback)
{
// stuff...
callback();
// other stuff...
}
На этом этапе вы отделили draw
функцию от контекста ее вызова. Вам нужно привязать функцию ( draw
) к ее контексту (экземпляру Display
).
Вы можете использовать Function.bind
, но для этого требуется поддержка JavaScript 1.8 (или просто используйте рекомендуемый патч).
Display.prototype.draw = function()
{
// snip...
window.webkitRequestAnimationFrame(this.draw.bind(this));
};
Комментарии:
1. Я пытаюсь передать,
display.draw
в какой функцииwebkitRequestAnimationFram
находится.2. О, я думаю, я вижу проблему: вы можете передать функцию, но
webkitRequestAnimationFrame
вызовете ее позже иthis
не укажете на нужный объект, потому что вы «отсоединили» функцию от ее объекта. Смотрите мою правку (в ожидании).3.
bind
Метод работает отлично (Никогда не знал об этом, спасибо :] ), но передачаthis.draw();
с закрытием все равно выдает ошибку.Uncaught TypeError: Object [object DOMWindow] has no method 'draw'
4. Хм, все еще не работает
Uncaught TypeError: Object #<Display> has no method 'fn'
но не беспокойтесь об этом, метод bind достаточно хорош. Спасибо за вашу помощь.5. Я не знал о методе Function.bind. Снимаю перед вами воображаемую шляпу! 🙂
Ответ №2:
Теперь, когда появился ES6 / 2015, если вы используете транспилятор, то функция arrow имеет лексическую this
привязку, поэтому вместо:
window.webkitRequestAnimationFrame(this.draw.bind(this));
вы можете сделать:
window.webkitRequestAnimationFrame(() => this.draw());
который немного чище.
Я эффективно использовал это при переносе Typescript на ES5.
Комментарии:
1. Это то, что я сейчас использую.
2. Я только что использовал этот метод, и мне пришлось ввести аргумент в функцию arrow, соответствующий аргументу timeStamp, который получает обратный вызов rAF ():
(t) => this.draw(t)
Ответ №3:
Я не могу гарантировать, что это хорошая идея и что я прав, но запуск .bind на каждом requestAnimationFrame означает создание новой функции на каждой итерации. Это просто звучит неправильно для меня.
Вот почему в моем проекте я кэшировал связанную функцию, чтобы избежать антишаблона.
Простой пример:
var Game = function () {
this.counter = 0;
this.loop = function () {
console.log(this.counter );
requestAnimationFrame(this.loop);
}.bind(this);
this.loop();
}
var gameOne = new Game();
Если у вас более сложный проект с наследованием прототипа, вы все равно можете создать кэшированную функцию с привязкой «this» в конструкторе объекта
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();
Мысли?
http://jsfiddle.net/3t9pboe8 / (посмотрите в консоли)
Комментарии:
1. Я модифицировал вашу скрипку , чтобы (я думаю) проверить разницу между кэшированием связанной функции и нет. Я был удивлен, увидев почти одинаковую производительность между ними (в Firefox 47, Chrome 52 и IE 11). Похоже, что функция, созданная
bind
, кэшируется браузером, но я не знаю, что происходит на самом деле. Я даже пытался запускать каждый из них по отдельности, чтобы избежать помех от оптимизации браузераrequestAnimationFrame
.2. @SeanH в данном случае «цикл» — это действительно простая функция, состоящая из увеличения счетчика и одной проверки условий. Интересно, будет ли использование «bind» на каждой итерации гораздо более сложной функции иметь большее значение. Вероятно, выигрыш от этой практики на requestAnimationFrame, который никогда не превышает 60 кадров в секунду, незначителен.
Ответ №4:
как насчет этого:
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) );
};
…предполагая, что вы используете jquery
Комментарии:
1. Как использовать его без jquery?? Я пытался использовать bind, но его ошибка, указывающая максимальный размер стека, превысила.
Ответ №5:
вы не должны использовать «это». Сделайте это простым.
var game = {
canvas:null,
context:null,
init:function(){
// init canvas, context, etc
},
update:function(){
//do something
game.render();
requestAnimationFrame(game.update, game.canvas);
},
};
Комментарии:
1. Неплохо. Мне нужен был метод, доступный извне класса, поэтому я использовал эту идиому:
var update = this.update = function() { render(); requestAnimationFrame(update); }
Ответ №6:
Помимо bind
метода и решения функции стрелки (предлагаемого ответом Jamaes World), другой (довольно старый) обходной путь может быть:
var self = this
window.webkitRequestAnimationFrame(
function() {
self.draw()
}
);
Ответ №7:
Также вы можете захотеть использовать оболочку requestAnimationFrame, чтобы заставить ее работать во всех браузерах https://github.com/kof/animation-frame