Prevent Requestanimationframe from Running All the Time

Prevent requestAnimationFrame from running all the time

You need to implement a condition so you can break the loop, for example (adopt as needed):

var isRunning = true;

function loop() {

... funky stuff here ...

/// test condition before looping
if (isRunning) requestAnimationFrame(loop);
}

Now when you set isRunning to false the loop will break. For convenience it's recommended that you have a method to start and stop the loop:

function startLoop(state) {

if (state && !isRunning) {
isRunning = true;
loop(); /// starts loop

} else if (!state && isRunning) {
isRunning = false;
}
}

The condition can be set by anything you need it to be set by, for example on a callback after an animation has finished etc. The important part is that the condition flag is available to both scopes using it (ie. most commonly in the global scope).

UPDATE:

More specific in this case is that your condition (radius) will never reach the condition required to eventually stop the loop.

Here is what you can do to fix this:

DEMO

var isPlaying = false;

function animate(){
/**
* To make sure you will reach the condition required you need
* to either make sure you have a fall-out for the steps or the
* step will become 0 not adding/subtracting anything so your
* checks below won't trigger. Here we can use a simple max of
* the step and a static value to make sure the value is always > 0
*/
if(mouseover){
radius += Math.max( ((targetRadius-radius)/ease)*speed, 0.5);
} else {
radius -= Math.max( ((radius-baseRadius)/ease)*speed, 0.5);
}

/**
* Now the checks will trigger properly and we can use the
* isPlaying flag to stop the loop when targets are reached.
*/
if(radius >= targetRadius) {
radius = targetRadius;
isPlaying = false; /// stop loop after this
} else if (radius <= baseRadius) {
radius = baseRadius;
isPlaying = false; /// stop loop after this
}

drawDday(radius);

/// loop?
if (isPlaying === true) requestAnimationFrame(animate);
}

In order to trigger the loop we use a method that will check if the loop is running, if not it will reset the isPlaying flag and start the loop. We do this inside both mouseover and mouseout:

canvas.on('mouseover', function(e){
mouseover = true;
startAnim();

}).on('mouseout', function(){
mouseover = false;
startAnim();
});

The method is simply checking isPlaying and if not set it set it to true and starts the loop - this so that the loop is only started once:

function startAnim() {
if (!isPlaying) {
isPlaying = true;
requestAnimationFrame(animate);
}
}

In the demo I added console logging to show when the loop is running and when targets are hit.

Hope this helps.

How to prevent requestAnimationFrame from redrawing canvas more then one time after clicking the button?

Since drawWind sets up the next call every time it's called, it makes sense that when you call it from the click handler, now you have two series of it running in parallel and things go twice as fast.

You said you tried cancelAnimationFrame but didn't show us that code. That is indeed how you would deal with this:

drawWind() {
cancelAnimationFrame(this.rafHandle);
this.rafHandle = requestAnimationFrame(this.drawWind.bind(this));

Now, when the button click calls drawWind while it has an rAF callback scheduled, it cancels that callback and sets a new one. So you still only have the one series running.

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.

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>

Cancel All Currently Running RequestAnimationFrames

You can call requestAnimationFrame, store the returned id, and then call cancelAnimationFrame for all positive integers less than that number.

function cancelAllAnimationFrames(){
var id = window.requestAnimationFrame(function(){});
while(id--){
window.cancelAnimationFrame(id);
}
}

Cancelling requestAnimationFrame after set amount of time

You can use requestAnimationFrame's argument which is elapsed time in milliseconds - this will give you a very accurate time:

function loop(elapsedTime) {

if (elapsedTime >= 4000) return; /// break loop after 4 seconds

requestAnimationFrame(loop); /// provides elapsed time as arg. to loop
}

/// start loop with rAF to get a valid argument first time:
requestAnimationFrame(loop);

Online demo here

From the documentation at MDN:

The callback method is passed a single argument, a
DOMHighResTimeStamp, which indicates the time, in milliseconds but
with a minimal precision of 10 µs, at which the repaint is scheduled
to occur.

Controlling fps with requestAnimationFrame?

How to throttle requestAnimationFrame to a specific frame rate

Demo throttling at 5 FPS: http://jsfiddle.net/m1erickson/CtsY3/

This method works by testing the elapsed time since executing the last frame loop.

Your drawing code executes only when your specified FPS interval has elapsed.

The first part of the code sets some variables used to calculate elapsed time.

var stop = false;
var frameCount = 0;
var $results = $("#results");
var fps, fpsInterval, startTime, now, then, elapsed;

// initialize the timer variables and start the animation

function startAnimating(fps) {
fpsInterval = 1000 / fps;
then = Date.now();
startTime = then;
animate();
}

And this code is the actual requestAnimationFrame loop which draws at your specified FPS.

// the animation loop calculates time elapsed since the last loop
// and only draws if your specified fps interval is achieved

function animate() {

// request another frame

requestAnimationFrame(animate);

// calc elapsed time since last loop

now = Date.now();
elapsed = now - then;

// if enough time has elapsed, draw the next frame

if (elapsed > fpsInterval) {

// Get ready for next frame by setting then=now, but also adjust for your
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
then = now - (elapsed % fpsInterval);

// Put your drawing code here

}
}

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.



Related Topics



Leave a reply



Submit