I have a image and I'm trying to animate it through setInterval() . My aim is to move the image up by a certain pixel only when the user clicks on the Canvas else the image should be moving downwards.Everything goes well but the timer increases the speed for every onclick()
my canvas tag
<canvas id="myCanvas" width="400" height="600" onclick=init()></canvas>
java script:
function init()
{
function draw1()
{
context.clearRect(0,0,400,600);
img_y = img_y - 40;
context.drawImage(image1,img_x,img_y);
}
function move()
{
context.clearRect(0,0,400,600);
img_y = img_y + 7;
context.drawImage(image1,img_x,img_y);
}
draw1();
setInterval(move,70);
}
My animation starts when the user clicks the Canvas of my game. When I click the Canvas for the second time or so,the animation speed increases. What is wrong with my logic?
Your problem is that each click ADDS an interval. So your code basically runs more times than you want (making your animation faster). Make sure to clear the interval before starting a new one. Try the following (docs):
window.myTimer = '';
function init()
{
function draw1()
{
context.clearRect(0,0,400,600);
img_y = img_y - 40;
context.drawImage(image1,img_x,img_y);
}
function move()
{
context.clearRect(0,0,400,600);
img_y = img_y + 7;
context.drawImage(image1,img_x,img_y);
}
draw1();
window.clearInterval(window.myTimer); // clear previous interval
window.myTimer = setInterval(move,70); // set it again
}
As #Pointy pointed out,, you are increasing the 'speed' every click, because you are creating a new interval which is calling that same function.
Consider either clearing the interval and making a new one each time : window.clearInterval(intervalId);
OR
implementing a bool within your function which will prevent its movement until click.
You are mixing up the code that initiates movement with the code that moves the object up. They need to be separate functions.
<canvas id="myCanvas" width="400" height="600" onclick="moveUp"></canvas>
JS:
var movement = 7, timer;
function animate() {
context.clearRect(0, 0, 400, 600);
img_y += 7;
context.drawImage(image1, img_x, img_y);
}
function moveUp() {
// no need to redraw the image here as it will be drawn at the next frame anyway
img_y -= 40;
}
function init() { // call this when you want the main animation to start
if (timer) { // if timer variable already exists
clearInterval(timer); // clear the corresponding interval
timer = null; // and also clear the variable itself
}
timer = setInterval(animate, 70);
}
(This code clears the timer before setting it, in case, for some reason, init is called more than once.)
You could also work with the onmouseup, onmousedown events, letting it move as long mouse is pressed, and stop moving when mouse is no longer pressed. This way you can create and remove the interval on both events
var interval;
function startInterval() {
interval = setInterval(animation.move.bind(animation), 70);
}
function stopInterval() {
clearInterval(interval);
}
var animation = {
old: undefined,
x: 0,
y: 0,
context: undefined,
move: function() {
this.y += 10;
animation.draw();
},
draw: function() {
if (typeof animation.context === 'undefined') {
var el = document.getElementById('ctx');
animation.context = el.getContext('2d');
}
var context = animation.context;
if (typeof animation.old !== 'undefined') {
context.clearRect(animation.old.x, animation.old.y, 10, 10);
}
animation.old = {
x: animation.x,
y: animation.y
};
context.fillStyle = "#FF0000";
context.fillRect(animation.old.x, animation.old.y, 10, 10);
}
};
<canvas id="ctx" width="300" height="300" onmousedown="startInterval()" onmouseup="stopInterval()"></canvas>
Related
I'm having multiple issues.
Everytime I click the animation goes faster. SOLVED #Jorge Fuentes González
Everytime I click the
last animation stops moving SOLVED #Kaiido
I have changed about everything I could think of around and still the same issue. Any help would be appreciated. Thanks!
function drawFrame(frameX, frameY, canvasX, canvasY) {
ctx.drawImage(img,
frameX * width, frameY * height,
width, height,
x_click, y_click,
scaledWidth, scaledHeight);
}
// Number of frames in animation
var cycleLoop = [3, 2, 1, 0, 7, 6, 5];
// Position of sprite in sheet
var currentLoopIndex = 0;
var frameCount = 0;
function step() {
frameCount++;
if (frameCount < 30) {
window.requestAnimationFrame(step);
return;
}
frameCount = 0;
// ctx.clearRect(0, 0, canvas.width, canvas.height);
drawFrame(cycleLoop[currentLoopIndex++], 0, 0, 0);
// Starts animation over
if (currentLoopIndex >= cycleLoop.length) {
// If you want to loop back in oposite direction after full animation
cycleLoop.reverse();
// Reseting position of which sprite to use
currentLoopIndex = 0;
}
window.requestAnimationFrame(step);
}
canvas.addEventListener("mousedown", getPosition, false);
function getPosition(event) {
x_click = event.x;
y_click = event.y;
x_click -= canvas.offsetLeft * 10;
y_click -= canvas.offsetTop * 10;
step();
}
==============================
JS Fiddle:
https://jsfiddle.net/HYUTS/q4fazt6L/9/
=======================================
Each time you click, you call step();, which will call window.requestAnimationFrame(step);, which will call step() the next animation frame. I don't see any stop point so the loop will be called forever.
So, when you call step() the first time, step() will be called continuously for ever, and if you click again, another step() "line" will be called a second time which will call window.requestAnimationFrame(step); for ever again, so now you will have two "lines" calling step(). That's why the animation goes faster, because on each animation frame step() will be called twice, doubling the calculations.
What you have to do is to check if the animation is already running (with a flag) and do not run it again, or to window.cancelAnimationFrame(ID) before starting the step() loop again. Note that on each click you must restart the variables that control the animation, like frameCount and currentLoopIndex
function drawFrame(frameX, frameY, canvasX, canvasY) {
ctx.drawImage(img,
frameX * width, frameY * height,
width, height,
x_click, y_click,
scaledWidth, scaledHeight);
}
// Number of frames in animation
var cycleLoop = [3, 2, 1, 0, 7, 6, 5];
// Position of sprite in sheet
var currentLoopIndex = 0;
var frameCount = 0;
var animationid = null;
function step() {
frameCount++;
if (frameCount < 30) {
animationid = window.requestAnimationFrame(step);
return;
}
frameCount = 0;
// ctx.clearRect(0, 0, canvas.width, canvas.height);
drawFrame(cycleLoop[currentLoopIndex++], 0, 0, 0);
// Starts animation over
if (currentLoopIndex >= cycleLoop.length) {
// If you want to loop back in oposite direction after full animation
cycleLoop.reverse();
// Reseting position of which sprite to use
currentLoopIndex = 0;
}
animationid = window.requestAnimationFrame(step);
}
canvas.addEventListener("mousedown", getPosition, false);
function getPosition(event) {
x_click = event.x;
y_click = event.y;
x_click -= canvas.offsetLeft * 10;
y_click -= canvas.offsetTop * 10;
frameCount = currentLoopIndex = 0;
window.cancelAnimationFrame(animationid);
step();
}
First step in your situation, is to create different objects for every animatables, so they can be drawn and updated independently.
After, you will have to split your logic in several parts.
A basic setup is to have one main loop that runs constantly in the background, and which will call all higher level objects update function, then all the drawing functions.
It's in these higher level methods that you will do the checks as to whether they should actually be discarded or not. The main loop doesn't have to take care of it.
In the example below, I created a class for your animatable objects. These objects will now have their own status, and will be able to update as they wish independently of others.
With this setup, adding a new Object in the scene is just a matter of pushing it in an Array.
// Our Animatable class (ES5 style...)
// Each object as its own frameCount and its own loopIndex
function Animatable(x, y) {
this.x = x;
this.y = y;
this.frameCount = 0;
this.loopIndex = 0;
this.cycleLoop = [3, 2, 1, 0, 7, 6, 5];
}
Animatable.prototype = {
update: function() {
this.frameCount++;
if (this.frameCount < 30) {
return;
}
this.frameCount = 0;
this.loopIndex++
if (this.loopIndex >= this.cycleLoop.length) {
// If you want to loop back in oposite direction after full animation
this.cycleLoop.reverse();
// Reseting position of which sprite to use
this.loopIndex = 0;
}
},
draw: function() {
// check the image is loaded
if (!img.naturalWidth) return;
var frameX = this.cycleLoop[this.loopIndex];
ctx.drawImage(img,
frameX * width, 0,
width, height,
this.x - scaledWidth/2, this.y - scaledHeight/2,
scaledWidth, scaledHeight);
}
};
// the main anim loop, independent
function startAnimLoop() {
animloop();
function animloop() {
requestAnimationFrame(animloop);
// updates
animatables.forEach(update);
// drawings
ctx.clearRect(0,0,canvas.width, canvas.height);
animatables.forEach(draw);
}
function update(animatable) {
animatable.update();
}
function draw(animatable) {
animatable.draw();
}
}
// one image for all
var img = new Image();
img.src = 'https://imgur.com/u2hjhwq.png';
img.onload = startAnimLoop;
// here we will hold all our objects
var animatables = [new Animatable(50, 50)]; // start with a single one
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
// some constant from OP's fiddle
var scale = 1.5;
var width = 100; // Bigger numbers push left <-, smaller right ->
var height = 100;
var scaledWidth = scale * width;
var scaledHeight = scale * height;
canvas.onclick = function(evt) {
var rect = canvas.getBoundingClientRect();
var x = evt.clientX - rect.left;
var y = evt.clientY - rect.top;
// we simply create a new object ;-)
animatables.push(new Animatable(x, y));
};
canvas{border:1px solid}
<canvas id="canvas" width="500" height="500"></canvas>
window.requestAnimationFrame is still running when you click again, and when you click you add another tick per frame to your animation, doubling your speed, as step() is called two times each frame now. You should cancel the previous animation frame when clicking again, using window.cancelAnimationFrame()
https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame
Like this:
...
var animationID;
//in step() save the id in every call
function step() {
...
animationID = window.requestAnimationFrame(step);
...
}
//In getPosition cancel the current animation
function.getPosition(event) {
...
window.cancelAnimationFrame(animationId);
...
}
And if you want multiple animations running, create an object for each and make the function step() their property, then run window.requestAnimationFrame(this.step) inside of step(). You'd also have to save every variable needed for the animation like currentLoopIndex as part of the object.
I am new to HTML 5 and JavaScript. I plan to move a line automatically on my canvas until I hit the stop button. So far, I have found an example that shows how to move a line continuously. I tried to add stop button functionality to this example.
However, the line stopped moving automatically. Instead, it moves a little bit each time I press stop. In order to find the error, I checked my developer console. The console suggested that maximum call stack size has exceeded.
Additionally, I plan to have two buttons that can move the line up and down is it possible? If yes, should pass a different points array to draw function. For example, if somebody clicks left, should I pass a new array in which x coordinate is fixed but y is increasing?
I have the following code:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<button type="button" id="stop">Stop</button>
<button type="button" id="left">Left</button>
<button type="button" id="right">Right</button>
<canvas id="canvas" width="600" height="400"></canvas>
<script>
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function ( /* function */ callback, /* DOMElement */ element) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas"), ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 200;
var points = [],
currentPoint = 1,
nextTime = new Date().getTime() + 500,
pace = 150;
// make some points
for (var i = 0; i < 50; i++) {
points.push({
x: i * (canvas.width / 50),
y: 100
});
}
function draw(runAnimation) {
if (runAnimation.value) {
if (new Date().getTime() > nextTime) {
nextTime = new Date().getTime() + pace;
currentPoint++;
if (currentPoint > points.length) {
currentPoint = 0;
}
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
ctx.lineWidth = 2;
ctx.strokeStyle = '#2068A8';
ctx.fillStyle = '#2068A8';
for (var p = 1, plen = currentPoint; p < plen; p++) {
ctx.lineTo(points[p].x, points[p].y);
}
ctx.stroke();
requestAnimFrame(draw(runAnimation));
}
}
var stop = document.getElementById('stop');
var left = document.getElementById('left');
var right = document.getElementById('right');
/*
* define the runAnimation boolean as an obect
* so that it can be modified by reference
*/
var runAnimation = {
value: false
};
stop.addEventListener('click', function () {
runAnimation.value = !runAnimation.value;
if (runAnimation.value) {
requestAnimationFrame(draw(runAnimation));
}
});
left.addEventListener('click', function () {
});
right.addEventListener('click', function () {
});
</script>
</body>
</html>
Your problem is where you call requestAnimationFrame..
You have
requestAnimFrame(draw(runAnimation));
The draw(runAnimation) is calling and running the function draw, which then gets down the the same line and does the same thing, the function never gets a chance to exit and eventually the call stack overflows.
To fix change the line to
requestAnimationFrame(draw);
Now you are just passing the referance to the function draw.
As you want the runAnimation value to be passed, you can not do this with requestAnimationFrame as it already has an argument passed to the draw function. (the time)
For this case it is easier for you just to change the function declaration from
function draw(runAnimation) { // runAnimation holds the time as that
// is what requestAnimationFrame will
// pass as the first argument
Change it to
function draw(time){ // you can ignore the time as you don't need it
The variable runAnimation can still be seen inside the function as you have defined it in the same scope.
And the last change is to the stop event
stop.addEventListener('click', function () {
runAnimation.value = !runAnimation.value;
if (runAnimation.value) {
requestAnimationFrame(draw(runAnimation)); // You are calling
// draw, but you
// should just pass
// a reference.
}
});
To
stop.addEventListener('click', function () {
runAnimation.value = !runAnimation.value;
if (runAnimation.value) {
requestAnimationFrame(draw); // only the function reference is
// needed.
}
});
I loop over a part of a HTML video and simultaneously draw a Canvas with the current frame of the video.
When the Videos starts over, there is always 1 gray frame at the canvas.
If the loop region is long, its not a big problem, but for my needs these regions are maybe 0.5 seconds and then the canvas starts to flicker if you loop over and over again.
When drawing the canvas, I also tried different video properties (ended, loop, networkState, readyState) - doesnt help
I provided a jsfiddle to show you my problem. (just press play at the video)
https://jsfiddle.net/Lz17fnf3/2/
$('#v').on('timeupdate', function () {
if ($('#v')[0].currentTime > 2) {//Loop for one second
$('#v')[0].currentTime = 1;
}
var $this = $('#v')[0]; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
drawCanvas();
setTimeout(loop, 1000 / 25); // drawing at 25fps
}
})();
});
function drawCanvas() {
var elem = document.getElementById('c');
var c = elem.getContext('2d');
var v = $('#v')[0];
$('#c').attr('width', v.videoWidth);
$('#c').attr('height', v.videoHeight);
if (v.readyState == 4) {
c.drawImage(v, 0, 0, v.videoWidth, v.videoHeight, 0, 0, v.videoWidth, v.videoHeight);
}
}
The reason it flickers is because when you assign the width or height to a canvas element, this action resets the entire context of the canvas, most likely that is causing the blank frame. Try moving all the canvas/context definitions outside the drawCanvas.
Something like:
var elem = document.getElementById('c');
var c = elem.getContext('2d');
var v = $('#v')[0];
// In order to set the canvas width & height, we need to wait for the video to load.
function init() {
if (v.readyState == 4) {
$('#c').attr('width', v.videoWidth);
$('#c').attr('height', v.videoHeight);
} else {
requestAnimationFrame(init);
}
}
init();
$('#v').on('timeupdate', function () {
if ($('#v')[0].currentTime > 2) { //Loop for one second
$('#v')[0].currentTime = 1;
}
var $this = $('#v')[0]; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
drawCanvas();
setTimeout(loop, 1000 / 25); // drawing at 25fps
}
})();
});
function drawCanvas() {
c.drawImage(v, 0, 0, v.videoWidth, v.videoHeight, 0, 0, v.videoWidth, v.videoHeight);
}
I have my object called player
var player = new Object();
player = {
x: (window.innerWidth / 2),
y: (window.innerHeight / 2),
width: 30,
height: 30,
color: 'red'
};
And some variable to check mouse down clicks
var mouseDown = 0;
And I have functions that is firing every 40ms...
function drawPlayer() {
var toPlayerX;
var toPlayerY;
var toPlayerLength;
toPlayerX = player.x - mouseX;
toPlayerY = player.y - mouseY;
toPlayerLength = Math.sqrt(toPlayerX * toPlayerX + toPlayerY * toPlayerY);
toPlayerX = toPlayerX / toPlayerLength;
toPlayerY = toPlayerY / toPlayerLength;
toPlayerLength = toPlayerLength - (toPlayerLength%1);
// this function get reduced distance between mouse and player canvas rect by 1px per click
function movePlayer() {
player.x -= toPlayerX;
player.y -= toPlayerY;
}
// on MOUSE DAWN EVENT
document.body.onmousedown = function() {
// on every mouse down click ++ mousedawn
++mouseDown;
// here we fire interval to make player alive and
// follow to mouse dawn click by ~25 pixels per secord
if (mouseDown==1) {
setInterval(movePlayer,40);
}
if (mouseDown>1) {
clearInterval(movePlayer());
mouseDown = 0;
}
};
}
The root of the problem is in this part
if (mouseDown>1) {
clearInterval(movePlayer());
mouseDown = 0;
}
I thought it will be clear movePlayer interval if mouseDown will have number of two, but instead the setInterval(movePlayer) just multiplie all the time while I click mouse, and making mouseDown = 0 works pretty well when it's go number of 2.
At the beginning I just wanna make code so when user clicks in some area the canvas player would follow straightly on area where the mouse was clicked and then stopped and while for example player is going somewhere already and the mouse was click again to change canvas first destination to area where the mouse was clicked last.
You have to set the setInterval command to a variable, and that is what you use to clear it.
So your code would look something like this:
// this function get reduced distance between mouse and player canvas rect by 1px per click
function movePlayer() {
player.x -= toPlayerX;
player.y -= toPlayerY;
}
var intervalID; // Declare the variable that is storing the interval.
// on MOUSE DAWN EVENT
document.body.onmousedown = function() {
// on every mouse down click ++ mousedawn
++mouseDown;
context.save();
context.fillStyle = 'black';
context.restore();
// here we fire interval to make player alive and follow to mouse dawn click by ~15 pixels per secord
if (mouseDown==1) {
intervalID = setInterval(movePlayer,40);
}
if (mouseDown>1) {
clearInterval(intervalID);
mouseDown = 0;
}
};
document.body.onmouseup = function() {
//--mouseDown;
};
clearInterval takes an intervalID returned from setInterval.
var timeoutId = 0;
if (mouseDown==1) {
timeoutId = setInterval(movePlayer,40);
}
if (mouseDown>1) {
clearInterval(timeoutId);
mouseDown = 0;
}
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval
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>
...
</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;
});
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.
The reason your animate function is being called continuously is because you start off by calling requestAnimationFrame(animate); and then each call to animate unconditionally calls requestAnimationFrame(animate); again. The cycle is never going to be broken unless you use cancelAnimationFrame at some point (which you don't), or make sure that animate only requests another frame if it's needed.
Another issue is the fact that radius will currently never reach either targetRadius nor baseRadius, and therefore neither of the following will ever be true:
if(radius > targetRadius) radius = targetRadius;
if(radius < baseRadius) radius = baseRadius;
This isn't directly responsible for the continual calls to animate, but since targetRadius and baseRadius are being used to indicate the end-points of your animation then we need to form some sort of sensible conditional with them.
So, you could do something like: http://jsfiddle.net/PLDUq/9/
var radius = baseRadius = 50,
targetRadius = 110,
ease = 50,
speed = 12,
currentAnim;
function animate(){
if(mouseover){
radius += ((targetRadius-radius)/ease)*speed;
} else {
radius -= ((radius-baseRadius)/ease)*speed;
}
drawDday(radius);
if(Math.round(radius) >= targetRadius) {
// uses Math.round() to ensure condition can be fulfilled
radius = targetRadius;
return; // doesn't call next frame
}
if(Math.round(radius) <= baseRadius) {
radius = baseRadius;
return; // doesn't call next frame
}
requestAnimationFrame(animate);
}
canvas.on('mouseenter mouseleave', function (e) {
if (currentAnim) {requestAnimationFrame(currentAnim);}
// cancels current animation if one is playing to
// prevent several concurrent loops calling animate()
mouseover = (e.type === 'mouseenter');
requestAnimationFrame(animate);
});