web-dev-qa-db-ja.com

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

これと作業バージョンの唯一の違いは、startAnimatingstopAnimatingのメソッドcallsstartおよびstopメソッドはコメント化されています。

63
corazza

そのため、さらにテストを行ったところ、実際に問題を引き起こしたのはアニメーションの停止ではなく、他のコードであることがわかりました(結局のところ、単純な再帰でした)。問題は、ページからレンダラーのdomElementを動的に追加および削除することでした。私がそれをやめた後、そうする理由は本当になかったし、初期化が起こっていた場所に一度含めたので、すべてがうまくいき始めました。

2
corazza

開始/停止する方法の1つは次のとおりです

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

停止は、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
18
user3363398

requestAnimationFrame polyfill gibhubページをご覧になることをお勧めします。これがどのように実装されるかについての議論があります。

2
Neil

2D Breakout Game のチュートリアルで遊んで、そこでrequestAnimationFrameも使用し、単純な return で停止しました。 returnの値が省略された場合、returnステートメントは関数の実行を終了します。

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

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