问题
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