Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop a requestAnimationFrame recursion/loop?

I'm using Three.js with the WebGL renderer to make a game which fullscreens when a play link is clicked. For animation, I use requestAnimationFrame.

I initiate it like this:

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

When I want to, I call the startAnimating method, and yes, it does work as intended. But, when I call the stopAnimating function, things break! There are no reported errors, though...

The setup is basically like this:

  • There is a play link on the page
  • Once the user clicks the link, a renderer's domElement should fullscreen, and it does
  • The startAnimating method is called and the renderer starts rendering stuff
  • Once escape is clicked, I register an fullscreenchange event and execute the stopAnimating method
  • The page tries to exit fullscreen, it does, but the entire document is completely blank

I'm pretty sure my other code is OK, and that I'm somehow stopping requestAnimationFrame in a wrong way. My explanation probably sucked, so I uploaded the code to my website, you can see it happening here: http://banehq.com/Placeholdername/main.html.

Here is the version where I don't try to call the animation methods, and fullscreening in and out works: http://banehq.com/Correct/Placeholdername/main.html.

Once play is clicked the first time, the game initializes and it's start method is executed. Once the fullscreen exits, the game's stop method is executed. Every other time that play has been clicked, the game only executes it's start method, because there is no need for it to be initialized again.

Here's how it looks:

var playLinkHasBeenClicked = function() {     if (!started)     {         started = true;          game = new Game(container); //"container" is an empty div     }      game.start(); } 

And here's how the start and stop methods look like:

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

The only difference between this and the working version is that startAnimating and stopAnimating method calls in start and stop methods are commented out.

like image 844
corazza Avatar asked May 24 '12 10:05

corazza


2 Answers

One way to start/stop is like this

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

Working example:

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>
like image 112
gman Avatar answered Oct 11 '22 12:10

gman


Stopping is as simple as not calling requestAnimationFrame anymore, and restarting is to call it it again. 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 
like image 32
user3363398 Avatar answered Oct 11 '22 14:10

user3363398