Подтвердить что ты не робот

Как остановить рекурсию/цикл 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 комментируются.

4b9b3361

Ответ 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. Есть дискуссии о том, как это реализовано.