Reputation: 17293
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?
Upvotes: 57
Views: 88582
Reputation: 4505
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 scale and any type (2D vector, 3D vector, ...).
For the quadratic easing in/out, the curve is separated in two distinct functions depending on the value of t
:
t
<= 0.5: f(x) = 2 * x * x
with x in [0;0.5] (graph)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;
}
Note: this is the same function as in @Toad's answer, but the bounds are different.
Another interesting blend curve is the one given by Bézier, which has 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);
}
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 sqr = t * t;
return sqr / (2.0f * (sqr - t) + 1.0f);
}
Upvotes: 109
Reputation: 1
This version allows you to use any ease in and ease out functions (EaseIn and EaseOut). Both functions must take a time value parameter from between 0 and 1, and return an eased time value between 0 and 1.
float EaseInOut(float t)
{
if (t <= 0.5f)
{
return EaseIn(t * 2) * 0.5f;
}
else
{
t -= 0.5f;
return (EaseOut(t * 2) * 0.5f) + 0.5f;
}
}
Upvotes: 0
Reputation: 1930
Here is a version with the amount of curvature as an argument, following this general solution linked to by Creak.
/*
* applyCurve: apply an S-curve to an input value.
* The highest positive curvature will result in a step from 0 to 1,
* the most negative curvature will result in a constant of 0.5.
*
* progress: the input value between 0 and 1,
* curvature: the amount of curvature between -1 and 1.
* Negative values curve the other way, 0 applies no curvature.
*/
double applyCurve(double progress, double curvature) {
assert(progress >= 0.0 && progress <= 1.0);
assert(curvature >= -1.0 && curvature <= 1.0);
if (curvature >= 0.0) {
if (curvature > 0.99999) return progress > 0.5 ? 1.0 : 0.0;
float exp = 1.0 / (1.0 - curvature); // find s-curve exponent
return pow(progress, exp) / (pow(progress, exp) + pow(1.0 - progress, exp)); // apply s-curve
} else {
if (curvature < -0.99999) return 0.5;
float exp = 1.0 + curvature; // find s-curve exponent
return pow(progress, exp) / (pow(progress, exp) + pow(1.0 - progress, exp)); // apply s-curve
}
}
Upvotes: 0
Reputation: 2146
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
Upvotes: 3
Reputation: 15925
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/
Upvotes: 49
Reputation: 317
I got same problem: wanted to animate my chart (Ease in-out)
.
Brainstorm gave me two ways:
1) Trygonometric formula. Firstly, I wrote y=(sin(x/π*10-π/2)+1)/2
,which analog is sin^2((5*x)/π)
float TrygoEase (float x) {
float y=(float)Math.pow(Math.sin(5*x/Math.PI),2);
return y;
}
2) Two parabolas. It was not hard. I just used y=2*x*x
on [0;0.5]
, and y=-2(x-1)^2+1
on [0.5;1]
float ParabolEase(float x) {
float y=2*x*x;
if(x>0.5f){
x-=1;
y=-2*x*x+1;
}
return y;
}
Use this ways for x=[0;1]
, what returns also y=[0;1]
.
Now You can compare this graphs:
Upvotes: 2