ahmd0
ahmd0

Reputation: 17293

Ease-in and ease-out animation formula

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

Answers (6)

Creak
Creak

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, ...).

Solution 1: Quadratic

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:

graph - part 1
graph - part 2

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.

Solution 2: Bézier

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:

Bezier curve

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:

curve

Which translates in C like this:

float ParametricBlend(float t)
{
    float sqr = t * t;
    return sqr / (2.0f * (sqr - t) + 1.0f);
}

Upvotes: 109

ImmortalMazeWalker
ImmortalMazeWalker

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

Mattijs
Mattijs

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

Oleksandr Poshtaruk
Oleksandr Poshtaruk

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

Toad
Toad

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

Egor
Egor

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:

enter image description here

Upvotes: 2

Related Questions