Ease-in and ease-out animation formula
Solution 1
Quadratic ease out where:
t = current time
b = start value
c = change in value
d = duration
function (float time, float startValue, float change, float duration) {
time /= duration / 2;
if (time < 1) {
return change / 2 * time * time + startValue;
}
time--;
return -change / 2 * (time * (time - 2) - 1) + startValue;
};
source: http://gizma.com/easing/
Solution 2
Personally, I'd rather use a function that gets a time in [0; 1] and output a value in [0; 1], so that we can apply the result to any type (2D vector, 3D vector, ...).
Solution 1
For the quadratic easing in/out, the curve is separated in two distinct functions depending on the value of t
:
- when
t
<= 0.5:f(x) = 2 * x * x
with x in [0;0.5] (graph) - when
t
> 0.5:f(x) = 2 * x * (1 - x) + 0.5
with x in [0;0.5] (graph)
Here are the graphs:
Since the second function is also in [0;0.5], but t
> 0.5 when we start to use it, we need to reduce t
by 0.5.
This is the result, in C:
float InOutQuadBlend(float t)
{
if(t <= 0.5f)
return 2.0f * t * t;
t -= 0.5f;
return 2.0f * t * (1.0f - t) + 0.5f;
}
Solution 2 (Bézier)
Another interesting blend curve is the one given by Bézier, which have the advantage to be quite optimized (no if). Here is the curve from Wolfram:
And here is the C code:
float BezierBlend(float t)
{
return t * t * (3.0f - 2.0f * t);
}
Solution 3 (parametric function)
Another method proposed by @DannyYaroslavski is the simple formula proposed here.
It is parametric and gets a nice in/out acceleration and deceleration.
With alpha = 2, you get this function:
Which translates in C like this:
float ParametricBlend(float t)
{
float sqt = t * t;
return sqt / (2.0f * (sqt - t) + 1.0f);
}
Edit 1: Add solution 3 from @DannyYaroslavski
Edit 2: Better explanation for solution 1
Edit 3: Add graphs to all solutions
Solution 3
All the above solutions lack examples of usage.
Found good solution here:
function animate({timing, draw, duration}) {
let start = performance.now();
requestAnimationFrame(function animate(time) {
// timeFraction goes from 0 to 1
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// calculate the current animation state
let progress = timing(timeFraction)
draw(progress); // draw it
if (timeFraction < 1) {
requestAnimationFrame(animate);
}
});
}
Example of usage:
animate({
duration: 1000,
timing(timeFraction) { // here you can put other functions
return timeFraction;
},
draw(progress) {
elem.style.width = progress * 100 + '%';
}
});
Other function:
function quad(timeFraction) {
return Math.pow(timeFraction, 2)
}
More here
Related videos on Youtube
ahmd0
Updated on May 01, 2021Comments
-
ahmd0 almost 3 years
Say, if I'm doing the Ease-Out and then Ease-In animation of an object's movement from X1 coordinate to X2 coordinate over S steps at equal time intervals. Can some suggest the formula to calculate this movement's X coordinates?
-
Matthew over 11 yearsCheck out robertpenner.com/easing, in particular the action script 2.0 source. From that you should be able to convert it to C#.
-
-
ahmd0 over 9 years
"quite optimized (no if)"
Are you kidding me? Do you know how much square root function is slower than a simpleif
? -
Creak over 9 yearsThat's what I said: sqr != sqrt ;)
-
Danny Yaroslavski almost 9 yearsThere is some bug in the InOutQuadBlend function, specifically in the second return. For example, at t=1, the last two lines will evaluate to 2*(.5)*(1-.5) = .5, and not the expected 1. I've found the formula shown at math.stackexchange.com/a/121755 does what Creak tries to do.
-
Creak almost 9 yearsYou're right @DannyYaroslavski, I changed the formula to fix that.
-
Sir over 8 yearsToad, when you say
t = time
do you mean time from start of animation or time from previous frame ? -
Toad over 8 yearst goes from 0 - 1 where 0 is the beginning of the animation, and 1 is the end. For every keyframe, you should change the values and let t again go from 0 to 1
-
starbeamrainbowlabs over 8 yearsWhat is the change in value? I don't understand where that comes from.
-
Toad over 8 yearsYou first use the formula to go from keyframe1 to keyframe 2. (So b is keyframe1 value and c is keyframe 2 value). Then you let the t go from 0.0 to 1.0. By the time you are at 1.0 you repeat these steps, only now you use keyframe2 and keyframe 3
-
Milad.Nozari about 8 yearsThis answer should get 1M thumbs ups. I looked 10 sources, found the same formula, but none of them mentioned what the hell t, b, c and d where. Thanks man
-
Myke Dev almost 7 yearsok, I kind of understand, this, but what do you do with the returned value?
-
Toad almost 7 yearssay you ease from the value b=8 to c=17. Then given the current t (time) in respect to the total duration(d) it returns the adjusted value which eases the motion instead of that it interpolates linearly
-
Coldsteel48 almost 7 yearsIn the last function I suppose that X is actually a T parameter of the funcrion, right ?
-
Toad over 6 yearsSay the startvalue = 3 and you want to ease to the value 5. Then the change in value is 2. So the change in value is the endvalue - the start value.
-
TimSim over 6 yearsI was confused because I thought the return value was by how much the initial value (what the startValue is based on) should change, but instead it's what that initial value should become. "return change" threw me off.
-
ygoe over 4 yearsThe second part on the original formula in solution 1 seems wrong. I don't understand it and Wolfram shows a completely wrong graph for it. The C code version is correct though but I also can't follow it. I'm trying to change this to a cubic function and am struggling.
-
Creak over 4 yearsYou're right @ygoe, I've fixed the formula and the explanation. Thank you!
-
SaganRitual over 2 yearsTried and tested in the wild in here 2021. Looks a beaut 🙏
-
ashleedawg about 2 yearsIn case anyone else in looking here are all 3 functions, in JavaScript and compressed . . . . . . . . .
function InOutQuad(n){return n<=.5?2*n*n:2*(n-=.5)*(1-n)+.5}
function Bezier(t){return t*t*(3-2*t)}
function Parametric(t){return t*t/(2*(t*t-t)+1)}
(Give 'em a number between 0 and +1 and they'll return a number between 0 and +1.)