【问题标题】:Angular component with requestAnimationFrame issue具有 requestAnimationFrame 问题的 Angular 组件
【发布时间】:2020-02-26 10:54:03
【问题描述】:

我使用 Angular 9 和 THREE.Js。该应用程序允许您通过单击按钮在 2D html 组件和 3D ThreeJs 组件之间切换。 3D 组件在ngAfterViewInit 上创建了threejs 所需的所有内容,并使用requestAnimationFrame 调用循环函数。

  private loop() {

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

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

一旦加载了我的场景的所有模型并触发了 onLoad 回调或触发了ngOnDestroy,循环就会取消。

  private stopLoop() {

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

这在不时切换组件时效果很好,但是当更快或连续多次单击按钮时,requestAnimationFrame 不会停止并累加。 我使用 stats-js lib 来检测帧速率,通常它在 40fps 左右,但它加起来可以达到 120-220fps 或更多。我的电脑开始卡顿和工作,一切都变慢了。

通常它应该为 0,因为当所有内容都加载完毕后,应该没有动画帧了!

我怎样才能避免这种行为?

更新: 好像我发现了这个问题。我的错误是bind 循环函数,它每次都会生成一个新实例。我有疑问,因为 AFID 仍在更新,因此取消了动画帧。但是不绑定会给我一些未定义属性的错误

有点难以 jsfiddle 所有这些,因为角部分

【问题讨论】:

  • 你能用一个活生生的例子来证明这个问题吗(例如jsfiddle.net/f2Lommf5)?使用cancelAnimationFrame 绝对是正确的方法,因此您的应用似乎出了点问题。

标签: angular three.js requestanimationframe


【解决方案1】:

removeEventListener 不同,cancelAnimationFrame 根本不在乎安排了什么功能,因此您的绑定内容无关紧要。

问题一定是你多次调用这个loop()方法,因此有几个这样的循环同时运行。

因此,请尝试找出不应该调用它的原因。

现在,避免这种情况的一种快速方法是在输入此loop() 方法时取消任何待处理的动画帧回调:

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>

【讨论】:

    猜你喜欢
    • 2017-08-25
    • 2013-10-24
    • 2012-10-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-16
    • 2017-02-20
    • 2018-09-16
    • 2013-01-18
    相关资源
    最近更新 更多