Angular component with requestAnimationFrame issue

半城伤御伤魂 提交于 2021-01-05 07:38:56

问题


im using Angular 9 together with THREE.Js. The app lets you switch between a 2D html component and a 3D ThreeJs component via button click. The 3D component creates all needed for threejs on ngAfterViewInit and calls a loop function with requestAnimationFrame.

  private loop() {

    this.AFID = requestAnimationFrame(this.loop.bind(this))

    this.renderer.render(this.scene, this.camera)
  }

The loop gets canceled as soon as all models of my scene are loaded and the onLoad callback is fired or when ngOnDestroy is fired.

  private stopLoop() {

    cancelAnimationFrame(this.AFID)
    this.AFID = null
  }

This works fine when switching components from time to time, but when clicking the button faster or several times in a row, the requestAnimationFrame won't stop and adds up. Im using the stats-js lib to detect frame rate, normally its around 40fps, but it adds up to 120-220fps or more. My computer starts to lag and work and everything slows down.

Normally it should be at 0 because when everything is loaded, there should be no animationframe anymore!

How can I avoid such behavior?

Update: Seems like I found the issue. My mistake was to bind the loop function, which generates a new instance everytime. Tho I have doubts because the AFID is still getting updated and therefore canceles the animationFrame. But to not bind gives me some errors of undefined properties here and there

Kinda hard to jsfiddle all of that, because of the angular part


回答1:


Unlike removeEventListener, cancelAnimationFrame doesn't care at all what function has been scheduled, so your binding thing is unrelated.

The problem must be that you call multiple times this loop() method, and thus have several such loop running concurrently.

So try to find what calls it when it shouldn't.

Now, one quick way to avoid this situation is to cancel any pending animation frame callback when entering this loop() method:

const checkbox = document.getElementById('check');
const start_btn = document.getElementById('start_btn');
const stop_btn = document.getElementById('stop_btn');
const instance = {
  loop(time) {
    if( checkbox.checked ) { // for demo only, you'd always want it
      cancelAnimationFrame( this.AFID ); // revoke any pending operation
    }
    this.AFID = requestAnimationFrame(this.loop.bind(this))

    countCallsPerFrame( time );
  },
  stop() {
    cancelAnimationFrame( this.AFID );
    logger.textContent = '\nstopped';
  }
};

// for demo let's call it three time at startup
for(let i=0; i<3; i++) {
  instance.loop();
}

start_btn.onclick = (evt) => instance.loop();
stop_btn.onclick = (evt) => instance.stop();
<label>Cancel pending frames at beginning<input type="checkbox" id="check"></label><br>
<button id="start_btn">instance.loop()</button><br>
<button id="stop_btn">instance.stop()</button><br>
<pre id="log"></pre>
<script>
  var logger = document.getElementById('log');
  let last_time;
  let iterations;
  function countCallsPerFrame( time ) {
    if( last_time !== time ) {
      last_time = time;
      iterations = 1;
    }
    else {
      iterations ++;
    }
    logger.textContent = `
Timestamp: ${ time | 0 }
called ${ iterations } time(s) per frame.`;
  };
</script>


来源:https://stackoverflow.com/questions/60412294/angular-component-with-requestanimationframe-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!