简体   繁体   English

如何停止 requestAnimationFrame 递归/循环?

[英]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.我正在使用 Three.js 和 WebGL 渲染器来制作一个点击play链接时全屏显示的游戏。 For animation, I use requestAnimationFrame .对于动画,我使用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.当我需要时,我会调用startAnimating方法,是的,它确实按预期工作。 But, when I call the stopAnimating function, things break!但是,当我调用stopAnimating函数时,事情就stopAnimating There are no reported errors, though...没有报告错误,但...

The setup is basically like this:设置基本上是这样的:

  • There is a play link on the page页面上有play链接
  • Once the user clicks the link, a renderer's domElement should fullscreen, and it does用户单击链接后,渲染器的domElement应全屏显示,并且确实如此
  • The startAnimating method is called and the renderer starts rendering stuff startAnimating方法被调用,渲染器开始渲染东西
  • Once escape is clicked, I register an fullscreenchange event and execute the stopAnimating method单击转义后,我注册一个fullscreenchange事件并执行stopAnimating方法
  • 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.我很确定我的其他代码没问题,而且我不知何故以错误的方式停止requestAnimationFrame My explanation probably sucked, so I uploaded the code to my website, you can see it happening here: http://banehq.com/Placeholdername/main.html .我的解释可能很糟糕,所以我将代码上传到我的网站,您可以在这里看到它发生的情况: 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 .这是我不尝试调用动画方法和全屏输入和输出工作的版本: http : //banehq.com/Correct/Placeholdername/main.html

Once play is clicked the first time, the game initializes and it's start method is executed.一旦第一次点击play ,游戏就会初始化并执行它的start方法。 Once the fullscreen exits, the game's stop method is executed.一旦全屏退出,就会执行游戏的stop方法。 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.每隔一次点击play ,游戏只执行它的start方法,因为不需要再次初始化它。

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:下面是startstop方法的样子:

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.此版本与工作版本之间的唯一区别是startAnimatingstopAnimating方法在startstop方法中的调用被注释掉了。

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>

Stopping is as simple as not calling requestAnimationFrame anymore, and restarting is to call it it again.停止就像不再调用 requestAnimationFrame 一样简单,而重新启动就是再次调用它。 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

I would suggest having a look at the requestAnimationFrame polyfill gibhub page.我建议查看requestAnimationFrame polyfill gibhub 页面。 There are discussions about how this is implemented.有关于如何实施的讨论。

So, after doing some more testing, I've found out that it was, indeed, my other code that posed a problem, not the animation stopping (it was a simple recursion after all).因此,在进行了更多测试之后,我发现确实是我的其他代码造成了问题,而不是动画停止(毕竟这是一个简单的递归)。 The problem was in dynamically adding and removing the renderer's domElement from the page.问题在于从页面动态添加和删除渲染器的 domElement。 After I've stopped doing that, for there was really no reason to do so, and included it once where the initialization was happening, everything started working fine.在我停止这样做之后,因为真的没有理由这样做,并且在初始化发生的地方包含它一次,一切都开始正常工作。

I played around with the tutorial of a 2D Breakout Game where they also used requestAnimationFrame and I stopped it with a simple return .我玩了一个2D Breakout Game的教程,他们也使用了 requestAnimationFrame ,我用一个简单的return停止了它。 The return statement ends function execution if the value of return is omitted.如果省略 return 的值,则 return 语句结束函数执行。

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

// looping the draw()
requestAnimationFrame(draw);
var myAnim //your requestId
function anim()
       {
       //bla bla bla
       //it's important to update the requestId each time you're calling reuestAnimationFrame
       myAnim=requestAnimationFrame(anim)
       }

Let's start it让我们开始吧

myAnim=requestAnimationFrame(anim)

Let's stop it让我们停下来

//the cancelation uses the last requestId
cancelAnimationFrame(myAnim)

Reference 参考

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM