Prevent requestAnimationFrame from running all the time
css, html5-canvas, javascript, jquery, requestanimationframe
Solution
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.
Problem
I'd like to know how to call the `animate` function through `requestAnimationFrame` only when it's realy needed. Currently the `animate` is called all the time what generates an overhead I guess. I already tried inside my `animate` function to compare `targetRadius` and the inital `radius` and return false once they are the same. Unfortunately this doesn't work at all. Can someone explain me how to solve that? jsfiddle HTML: ``` <canvas id="ddayCanvas" width="288" height="288" data-image="http://www.topdesignmag.com/wp-content/uploads/2011/07/64.png"> <div> <div class="product-image"></div> <div class="product-box">...</div> <a href="#" class="overlay">...</a> </div> </canvas> ``` JS: ``` // Options var maxImageWidth = 250, maxImageHeight = 196; var canvas = $('#ddayCanvas'), canvasWidth = canvas.width(), canvasHeight = canvas.height(), sectorColor = $('.product-box').css('background-color'), context = canvas[0].getContext('2d'), imageSrc = canvas.data('image'), imageObj = new Image(), imageWidth, imageHeight, mouseover = false; imageObj.onload = function() { imageWidth = this.width; imageHeight = this.height; if (imageWidth > maxImageWidth){ imageHeight = imageHeight - (imageWidth - maxImageWidth); imageWidth = maxImageWidth; } if (imageHeight > maxImageHeight) { imageWidth = imageWidth - (imageHeight - maxImageHeight); imageHeight = maxImageHeight; } drawDday(90); }; imageObj.src = imageSrc; function drawDday (radius) { context.clearRect(0, 0, canvasWidth, canvasHeight); context.drawImage(imageObj, Math.ceil((canvasWidth - imageWidth) / 2), Math.ceil((canvasHeight - imageHeight) / 2), imageWidth, imageHeight); context.fillStyle = sectorColor; context.beginPath(); context.rect(0, 0, canvasWidth, canvasHeight); context.arc(canvasWidth/2, canvasHeight/2, radius, 0, Math.PI*2, true); context.closePath(); context.fill(); // Check out the console console.log('test'); } var radius = baseRadius = 90, targetRadius = 110, ease = 50, speed = 2; function animate(){ if(mouseover){ radius += ((targetRadius-radius)/ease)*speed; } else { radius -= ((radius-baseRadius)/ease)*speed; } if(radius > targetRadius) radius = targetRadius; if(radius < baseRadius) radius = baseRadius; drawDday(radius); requestAnimationFrame(animate); } requestAnimationFrame(animate); canvas.on('mouseover', function(e){ mouseover = true; }).on('mouseout', function(){ mouseover = false; }); ```