How to stop recursive loop with `requestAnimationFrame`
First, it's not really recursive since it queues the callback on the event loop rather than calling the callback directly. So you don't have to worry about running out of memory on the call stack.
To stop calling requestAnimationFrame, you simply don't call it. The question is, when do you want to stop calling it? If you are providing a utility function for someone else, you usually let them dictate when to "unsubscribe" or stop the updates.
private recursiveLoopWithDelay(loopFn: any, delay: number) {
const self = this;
let stamp = Date.now();
function _loop() {
// If we aren't looping anymore, just exit the code.
// Don't requeue requestAnimationFrame
if (!self.isLoopOn) {
return;
}
if (Date.now() - stamp >= delay) {
loopFn();
stamp = Date.now();
}
window.requestAnimationFrame(_loop);
}
window.requestAnimationFrame(_loop);
}
You can also skip binding by using Lexical scope like I've done here. Storing this
in a variable self
that I can lookup at any point.
How to stop a requestAnimationFrame recursion/loop?
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. 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.
Cancel the requestAnimationFrame loop after some time
Just use a variable to reference the current render function and change that variable on the timer event.
Example
var currentFrameRender = animate; // set current render
requestAnimationFrame(currentFrameRender); // request first frame
setTimeout(() => currentFrameRender = update ,5000); // switch render in 5s
setTimeout(() => currentFrameRender = undefined ,10000); // stop animation in 10s
function animate(){
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
for(let i = 0; i < circles.length; i++){
circleArray[i].scatter();
}
// request frame only if currentFrameRender is defined
currentFrameRender && requestAnimationFrame(currentFrameRender);
}
function update(){
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
for(let i = 0; i < circles.length; i++){
circleArray[i].update();
}
// request frame only if currentFrameRender is defined
currentFrameRender && requestAnimationFrame(currentFrameRender);
}
How to stop a requestAnimationFrame on canvas in JavaScript
You can use cancelAnimationFrame()
to stop the animation. You can assign requestAnimationFrame()
to a variable and then to stop the animation call that variable in the cancel function. You also don't need to call requestAnimationFrame()
in the mouseDown function since just calling draw()
will trigger it to be activated.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width" />
<head>
<title>Parking Master LVL. 1</title>
</head>
<body>
<h3 id="header-style">Parking Master</h3>
<p class="paraGraph1">How to play:</p>
<ol id="directions">
<p>1. drive into the Green Outlined Square</p>
<p>2. do not crash through the process or the game ends!</p>
<p>3. press "<span><button style="position: relative;
top: -3px;
color: #11fc05;
border: 2px double #11fc05; width: 1.2em; height: 1.2em;"><span style="position: absolute; left: 4px;top: 0px; font-size: 0.6em;">Park</span></button></span>" when you parked in the correct spot.</p>
</ol>
</body>
<link rel="stylesheet" href="style.css">
<!--Game-->
<canvas id="myCanvas" width="600" height="400" style="border:2px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<button style="position:absolute;left:550px;top:600px;" class="buttonDrive" onmousedown="mouseDown()" onmouseup="mouseUp()">Drive</button>
<button class="buttonPark" onclick="parkDetector()" style="position: absolute;left: 550px; top: 540px;"><strong>Park</strong></button>
<div id="Car-Body">
<script>
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
x = 0,
last = performance.now(),
animate;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.rect(x, 20, 20, 20);
ctx.fillStyle = "#000000";
ctx.fill();
x += 0.5;
animate = requestAnimationFrame(draw);
}
</script>
</div>
<script>
var continueAnimating = true;
var carBody = document.getElementById("Car-Body");
// detect if car is parked
function parkDetector() {
if (carBody > 0) {
// If the car Parked in the correct spot
carIsParked();
}
}
// If the car is not parked in the correct spot
if (carBody < 176) {
function carIsNotParked() {
alert("Hmm... it doesn't seem like you parked!");
}
}
function carIsParked() {
alert("You Parked!");
document.body.innerHTML += p;
document.body.innerHTML += '<button onclick="nextLevel()">Next Level</button>';
}
// Direct the car
var p = '<div></div>';
// Redirect to Next Level
function nextLevel() {
document.getElementsByClassName().innerHTML = '<a href="https://sites.google.com/view/parking-master-lvl2">Next Level</a>';
}
function mouseUp() {
console.log('wow');
cancelAnimationFrame(animate) // if the "Drive" button is let go of, cancel the animation
}
function mouseDown() {
draw();
// if "Drive" is pressed, draw the animation
}
</script>
Can't stop requestAnimationFrame (Javascript/html5: canvas)
var active = true;
function gameloop(){
if(active){
window.requestAnimationFrame(gameloop);
}
}
Why does calling requestAnimationFrame at the beginning of a loop not cause infinite recursion?
Please let me know if I am completely off-base; I haven't used the animation stuff before. An example I saw for using requestAnimationFrame
is:
(function animloop(){
requestAnimFrame(animloop);
render();
})();
Are you wondering why animloop
as it is passed into requestAnimFrame
doesn't cause an infinite loop when it is subsequently called?
This is because this function isn't truly recursive. You might be thinking that animloop
is immediately called when you call requestAnimFrame
. Not so! requestAnimFrame
is asynchronous. So the statements are executed in the order that you see. What this means is that the main thread does not wait for the call to requestAnimFrame
to return, before the call to render()
. So render()
is called almost immediately. However the callback (which in this case is animloop
) is not called immediately. It may be called at some point in the future when you have already exited from the first call to animloop
. This new call to animloop
has its own context and stack since it hasn't been actually called from within the execution context of the first animloop
call. This is why you don't end up with infinite recursion and a stack overflow.
Right usage of requestAnimationFrame / cancelAnimationFrame
You should send the id of the requestAnimationFrame
you want to cancel into cancelAnimationFrame
. That id is a return value of the original requestAnimationFrame
.
So request one animation loop:
var id = requestAnimationFrame(cache.mechanism.snowFall);
And to cancel that request:
cancelAnimationFrame(id);
Alternatively, since requestAnimationFrame
must be called to keep the loop running, you could use a flag to stop the animation:
// Set an external flag to allow animations to continue
var continueAnimating = true;
function animate()
{
if(continueAnimating)
{
// when continueAnimating is false, this new
// request will not occur and animation stops
requestAnimationFrame(animate);
}
}
// To turn off animation
continueAnimating = false;
How do I stop requestAnimationFrame
var id;
function animate() {
id = requestAnimationFrame( animate );
renderer.render( scene, camera );
}
To cancel
cancelAnimationFrame( id );
three.js r.55
Is requestAnimationFrame implementation's recursive?
This only requests the browser to call your callback before the next rendering loop:
You should call this method whenever you're ready to update your animation onscreen. This will request that your animation function be called before the browser performs the next repaint.
So there is no recursion here, and your function continue with the execution.
You can also cancel the request for your callback with cancelAnimationFrame
.
Look here.
Related Topics
Style.Display='None' Doesn't Work on Option Tags in Chrome, But It Does in Firefox
Bootstrap Dropdown Closing When Clicked
Clearing My Form Inputs After Submission
Saving and Loading an Image from Localstorage
Possible to Associate Label with Checkbox Without Using "For=Id"
Img-Src' Was Not Explicitly Set, So 'Default-Src' Is Used as a Fallback
Multiple Select Limit Number of Selection
How to Take a Snapshot of HTML5-Javascript-Based Video Player
How to Disable an Input Type=Text
Inlining Ecmascript Modules in HTML
Jquery Duplicate Div into Another Div
Force Page Zoom at 100% with Js
Make Checkbox Behave Like Radio Buttons with JavaScript
How to Filter Input Type="File" Dialog by Specific File Type
Trigger Autocomplete Without Submitting a Form