web-dev-qa-db-fra.com

Comment arrêter une récursivité / boucle requestAnimationFrame?

J'utilise Three.js avec le moteur de rendu WebGL pour créer un jeu qui affiche en plein écran lorsqu'un lien play est cliqué. Pour l'animation, j'utilise requestAnimationFrame.

Je l'initie comme ça:

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

Lorsque je le souhaite, j'appelle la méthode startAnimating et, oui, cela fonctionne comme prévu. Mais lorsque j'appelle la fonction stopAnimating, les choses se cassent! Il n'y a pas d'erreur signalée, cependant ...

La configuration est fondamentalement comme ceci:

  • Il y a un lien play sur la page
  • Une fois que l'utilisateur a cliqué sur le lien, le rendu du rendu domElement doit être en plein écran, et
  • La méthode startAnimating est appelée et le moteur de rendu commence le rendu
  • Une fois que vous avez cliqué sur Echap, j'enregistre un événement fullscreenchange et exécute la méthode stopAnimating.
  • La page tente de quitter le mode plein écran, mais le document entier est complètement vide.

Je suis à peu près sûr que mon autre code est correct et que j'arrête en quelque sorte requestAnimationFrame. Mon explication était probablement nulle, alors j'ai téléchargé le code sur mon site Web, vous pouvez le voir se passer ici: http://banehq.com/Placeholdername/main.html .

Voici la version où je n’essaie pas d’appeler les méthodes d’animation, et le plein écran fonctionne: http://banehq.com/Correct/Placeholdername/main.html .

Une fois que play est cliqué pour la première fois, le jeu s'initialise et sa méthode start est exécutée. Une fois le mode plein écran terminé, la méthode stop du jeu est exécutée. Chaque fois que l'utilisateur clique sur play, le jeu n'exécute que la méthode start, car il n'est pas nécessaire de l'initialiser à nouveau.

Voici à quoi ça ressemble:

var playLinkHasBeenClicked = function()
{
    if (!started)
    {
        started = true;

        game = new Game(container); //"container" is an empty div
    }

    game.start();
}

Et voici à quoi ressemblent les méthodes start et stop:

self.start = function()
{
    self.container.appendChild(game.renderer.domElement); //Add the renderer's 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();
}

La seule différence entre cette version et la version de travail est que startAnimating et la méthode stopAnimating appelle dans start et les méthodes stop sont commentées.

63
corazza

Donc, après avoir fait quelques tests supplémentaires, j'ai découvert que c'était bien mon autre code qui posait problème, pas l'arrêt de l'animation (c'était une simple récursion après tout). Le problème tenait à ajouter et à supprimer dynamiquement le domElement du rendu de la page. Après avoir arrêté de le faire, car il n'y avait vraiment aucune raison de le faire, et l'inclure une fois l'initialisation en cours, tout a commencé à bien fonctionner.

2
corazza

Une façon de démarrer/arrêter est comme ça

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

Exemple de travail:

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>
112
gman

Arrêter est aussi simple que de ne plus appeler requestAnimationFrame, et le redémarrer consiste à l'appeler à nouveau. ex)

        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
18
user3363398

Je suggérerais de jeter un oeil à la page requestAnimationFrame polyfill gibhub. Il y a des discussions sur la façon dont cela est mis en œuvre.

2
Neil

J'ai joué avec le tutoriel d'un 2D Breakout Game où ils ont également utilisé requestAnimationFrame et je l'ai arrêté avec un simple retour . L'instruction return met fin à l'exécution de la fonction si la valeur de return est omise.

if(!lives) {
    alert("GAME OVER");
    return;
}

// looping the draw()
requestAnimationFrame(draw);
2
SaikaVA