Как остановить рекурсию/цикл requestAnimationFrame?
Я использую Three.js с визуализатором WebGL для создания игры, в которой полноэкранные экраны при нажатии ссылки play
. Для анимации я использую requestAnimationFrame
.
Я начинаю его следующим образом:
self.animate = function()
{
self.camera.lookAt(self.scene.position);
self.renderer.render(self.scene, self.camera);
if (self.willAnimate)
window.requestAnimationFrame(self.animate, self.renderer.domElement);
}
self.startAnimating = function()
{
self.willAnimate = true;
self.animate();
}
self.stopAnimating = function()
{
self.willAnimate = false;
}
Когда я захочу, я вызываю метод startAnimating
, и да, он работает по назначению. Но, когда я называю функцию stopAnimating
, все ломается! Ошибок не сообщалось, хотя...
Настройка в основном такова:
- На странице есть ссылка
play
- Как только пользователь нажимает на ссылку, рендерер
domElement
должен иметь полноэкранный режим, и он выполняет
- Вызывается метод
startAnimating
, и рендеринг начинает рисовать материал
- После щелчка по экрану я регистрирую событие
fullscreenchange
и выполняю метод stopAnimating
- Страница пытается выйти из полноэкранного режима, но весь документ полностью пуст.
Я уверен, что мой другой код в порядке, и я как-то останавливаю requestAnimationFrame
неправильно. Мое объяснение, вероятно, было сосано, поэтому я загрузил код на свой сайт, вы можете увидеть, как это происходит здесь: http://banehq.com/Placeholdername/main.html.
Вот версия, в которой я не пытаюсь вызвать методы анимации, и полноэкранная работа внутри и снаружи: http://banehq.com/Correct/Placeholdername/main.html.
После первого нажатия кнопки play
, игра инициализируется и выполняется start
. После выхода из полноэкранного режима выполняется игра stop
. Каждый раз, когда нажата кнопка play
, игра выполняет только этот метод start
, потому что нет необходимости снова ее инициализировать.
Вот как это выглядит:
var playLinkHasBeenClicked = function()
{
if (!started)
{
started = true;
game = new Game(container); //"container" is an empty div
}
game.start();
}
И вот как выглядят методы start
и stop
:
self.start = function()
{
self.container.appendChild(game.renderer.domElement); //Add the renderer domElement to an empty div
THREEx.FullScreen.request(self.container); //Request fullscreen on the div
self.renderer.setSize(screen.width, screen.height); //Adjust screensize
self.startAnimating();
}
self.stop = function()
{
self.container.removeChild(game.renderer.domElement); //Remove the renderer from the div
self.renderer.setSize(0, 0); //I guess this isn't needed, but welp
self.stopAnimating();
}
Единственная разница между этой и рабочей версией заключается в том, что методы startAnimating
и stopAnimating
метода вызовы в start
и stop
комментируются.
Ответы
Ответ 1
Итак, после проведения еще нескольких тестов, я узнал, что это был, действительно, мой другой код, который представлял проблему, а не остановку анимации (в конце концов, это была простая рекурсия). Проблема заключалась в динамическом добавлении и удалении domElement объекта рендеринга со страницы. После того, как я прекратил это делать, потому что не было причин для этого, и включил его однажды, когда происходила инициализация, все стало нормально работать.
Ответ 2
Один из способов запуска/остановки - это
var requestId;
function loop(time) {
requestId = undefined;
...
// do stuff
...
start();
}
function start() {
if (!requestId) {
requestId = window.requestAnimationFrame(loop);
}
}
function stop() {
if (requestId) {
window.cancelAnimationFrame(requestId);
requestId = undefined;
}
}
Рабочий пример:
const timeElem = document.querySelector("#time");
var requestId;
function loop(time) {
requestId = undefined;
doStuff(time)
start();
}
function start() {
if (!requestId) {
requestId = window.requestAnimationFrame(loop);
}
}
function stop() {
if (requestId) {
window.cancelAnimationFrame(requestId);
requestId = undefined;
}
}
function doStuff(time) {
timeElem.textContent = (time * 0.001).toFixed(2);
}
document.querySelector("#start").addEventListener('click', function() {
start();
});
document.querySelector("#stop").addEventListener('click', function() {
stop();
});
<button id="start">start</button>
<button id="stop">stop</button>
<div id="time"></div>
Ответ 3
Остановка так же просто, как больше не вызывать requestAnimationFrame, а перезапуск - снова вызвать ее.
ех)
var pause = false;
function loop(){
//... your stuff;
if(pause) return;
window.requestionAnimationFrame(loop);
}
loop(); //to start it off
pause = true; //to stop it
loop(); //to restart it
Ответ 4
Я бы предложил посмотреть страницу requestAnimationFrame polyfill gibhub. Есть дискуссии о том, как это реализовано.