What is an easing function?

38.7k views Asked by At

What is meant by easing function in the context of animation. It seems that dojo, jquery, silverlight, flex and other UI systems have the notion of easing function. I could not locate a good explanation of easing functions? Can anyone explain the concept of easing functions, or point a good explanation of them, I am interested in the concept not in the specific details of a framework?

Is easing strictly used for location or is it general and can be applied to any property of an object?

5

There are 5 answers

3
J. Holmes On BEST ANSWER

An easing function is usually a function that describes the value of a property given a percentage of completeness. Different frameworks use slightly different variations, but the concept is easy to grasp once you get the idea, but it's probably best to look a few examples.

First lets look at the interface that all our easing functions will abide by.

Our easing functions will take several arguments:

  • percentComplete: (0.0 to 1.0).
  • elapsedTime: The number of milliseconds the animation has been running
  • startValue: the value to start at (or the value when the percent complete is 0%)
  • endValue: the value to end at (or the value when the percent complete is 100%)
  • totalDuration: The total desired length of the animation in milliseconds

And will return a number which represents the value the property should be set to.

Note: this is the same signature that jQuery uses for its easing functions, which I'll be borrowing for examples.

The easiest to understand is a linear ease:

var linear = function(percent,elapsed,start,end,total) {
    return start+(end-start)*percent;
}

And now to put this to use:

Lets say we had an animation that was going to go for 1000 milliseconds and was supposed to start at 0 and end at 50. Passing those values into our easing function should tell us what the actual value should be:

linear(0, 0, 0,50, 1000)        // 0
linear(0.25, 250, 0, 50, 1000)  // 12.5
linear(0.5, 500, 0, 50, 1000)   // 25
linear(0.75, 750, 0, 50, 1000)  // 37.5
linear(1.0, 1000, 0, 50, 1000)  // 50

This is a pretty straight forward (no pun intended) tween. It is a simple linear interpolation. If you were to graph value vs time, it would be a straight line:

Linear ease

Lets take a look at a bit more complicated easing function, a quadratic ease in:

var easeInQuad = function (x, t, b, c, d) {
    return c*(t/=d)*t + b;
}

And lets look at the same results, using the same inputs as before:

easeInQuad(0, 0, 0, 50, 1000)      // 0
easeInQuad(0.25, 250, 0, 50, 1000) // 3.125
easeInQuad(0.5, 500, 0, 50, 1000)  // 12.5
easeInQuad(0.75, 750, 0, 50, 1000) // 28.125
easeInQuad(1, 1000, 0, 50, 1000)   // 50

Notice the values are very different than our linear ease. It starts out very slow, then accelerates to its ending point. At 50% completion of the animation it has only made it to a value of 12.5, which is one quarter of the actual distance between the start and end values we have specified.

If we were to graph this function it would look something like this:

Quad-Ease-In

Now lets take a look at a basic ease-out:

var easeOutQuad = function (x, t, b, c, d) {
    return -c *(t/=d)*(t-2) + b;
};

This essentially does the "opposite" acceleration curve of an ease in. It starts out fast and then decelerates to its ending value:

Ease out

And then there are functions that ease both in and out:

var easeInOutQuad = function (x, t, b, c, d) {
    if ((t/=d/2) < 1) return c/2*t*t + b;
    return -c/2 * ((--t)*(t-2) - 1) + b;
};

EaseInOut

This function will start out slow and end slow, reaching its maximum velocity in the middle.

There are a bunch of easing/interpolations that you can use: Linear, Quadradic, Cubic, Quart, Quint, Sine. And there are specialty easing functions like Bounce and elastic, which have their own.

For example, an elastic ease in:

var easeInElastic = function (x, t, b, c, d) {
    var s=1.70158;var p=0;var a=c;
    if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
    if (a < Math.abs(c)) { a=c; var s=p/4; }
    else var s = p/(2*Math.PI) * Math.asin (c/a);
    return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},

Elastic ease in

Perhaps somebody else can explain the actual math part behind the interpolation, because honestly I'm not a math wiz. But that's the basic principle of the easing functions themselves.

When you start a tween/animation, the animation engine remembers the start and end values you want. Then each time it updates, its figures out of how much time has passed. It call the supplied easing function with the values to figure out the value the property should be set to. As long as all of the easing functions implement the same signature, they can be swapped out with ease, and the core animation engine doesn't have to know difference. (Which makes for an excellent separation of concerns).

You'll notice that I've avoided talking about x and y positions explicitly, because easing doesn't have anything specifically to do with position per se. An easing function just defines a transition between a start and end values. Those could be x coordinates, or a color, or the transparency of an object.

And in fact, in theory, you could apply different easing function to interpolate for different properties. Hopefully this helps shed some light on the basic idea.

And here is a really cool example (that uses a slightly different signature, but is the same principle) to play with to get the idea of how easing relates to position.


Edit

Here is a little jsFiddle I threw together to demonstrate some of the basic usages in javascript. Notice that the top property is tweened using bounce, and the left property is tweened using a quad. Use the slider to simulate the render loop.

Since all the functions in the easing object have the same signature, you can swap any of them out for each other. Right now most of these things are all hard-coded (things like start and end values, the tween functions that are used and the length of the animation), but in a real-world example of a animation helper, you would want to pass in the following properties:

  • The property to be changed
  • The start value (or if left undefined then use its current value)
  • The end value
  • The length the animation should be
  • The reference to the tweening function you want to use.

The animation engine would keep track of these settings for the duration of the animation and during every update cycle, it would use the tweening argument to calculate the properties new value.

3
Justin Niessner On

An easing function is an algorithm that controls the speed of an animation to give a desired effect (bouncing, zoom in and slow, etc.).

Check out what MSDN has to say about them for a little more detail.

0
Chris Lacasse On

It is a property (size, shape, location) transition from one state to another.

Here are some neat little graphs describing the easing functions offered by jquery ui.

http://jqueryui.com/demos/effect/easing.html

3
Kaaf On

tl;dr basic implantation of the ease function assuming normalized 0-1 range with strength support:

t = normalized input value, so from 0-1.

s = strength, where 1 is linear and the higher it is, the stronger the easing effect.

# ease-in
result = t**s 

# ease-out
s = int(s)*2 + 1
result = s*((t-1)**s + 1)

# ease-inOut
result = t**s /(t**s + (1-t)**s)
0
akinuri On

I'd like to post my answer to this old question even though it has an accepted answer. 32bitkid has made the necessary explanation. What I'll add is the basic practical implementation, because I couldn't find one (which I also posted a question about it).

Take this simple linear animation, for example. I doubt it requires any explanations since the code is self-explanatory. We calculate a constant increment value which doesn't change over time and at each iteration, we increase the position of the box. We're modifying the position variable directly and then applying it on the box.

JSFiddle

var box = document.getElementById("box");

var fps           = 60;
var duration      = 2;                                   // seconds
var iterations    = fps * duration;                      // 120 frames
var startPosition = 0;                                   // left end of the screen
var endPosition   = window.innerWidth - box.clientWidth; // right end of the screen
var distance      = endPosition - startPosition;         // total distance
var posIncrement  = distance / iterations;               // change per frame
var position      = startPosition;                       // current position

function move() {
  position += posIncrement;              // increase position
  if (position >= endPosition) {         // check if reached endPosition
    clearInterval(handler);              // if so, stop interval
    box.style.left = endPosition + "px"; // jump to endPosition
    return;                              // exit function
  }
  box.style.left = position + "px";      // move to the new position
}

var handler = setInterval(move, 1000/fps); // run move() every 16~ millisecond
body {
    background: gainsboro;
}
#box {
    width: 100px;
    height: 100px;
    background: white;
    box-shadow: 1px 1px 1px rgba(0,0,0,.2);
    position: absolute;
    left: 0;
}
<div id="box"></div>


Now, let's add easing. We start simple by using linear (no-easing). It'll result the same animation above, but the approach is different. This time, we won't be modifying the position variable directly. What we'll be modifying is time.

function linear(time, begin, change, duration) {
    return change * (time / duration) + begin;
}

First, let's talk about the parameters.

  • time: the elapsed time
  • begin: initial value of a property (width, left, margin, opacity, etc.)
  • change: displacement, (end value - start value)
  • duration: total time the animation will take

time and duration are directly related. If you have a 2 second animation, you increase time and pass it to the easing function linear. The function will return a position which indicates that the box should be at that position at the given time.

Let's say I'm moving a box from 0 to 100 in 2 seconds. If I want to get the position of the box, say at the 700 millisecond, I'd call the linear function in the following way:

linear(0.7, 0, 100, 2);

which would return 35. 700 milliseconds after the animation starts, box's position will be at 35px. Let's see this in action.

JSFiddle

var box = document.getElementById("box");

var fps           = 60;
var duration      = 2;                                   // seconds
var iterations    = fps * duration;                      // 120 frames
var startPosition = 0;                                   // left end of the screen
var endPosition   = window.innerWidth - box.clientWidth; // right end of the screen
var distance      = endPosition - startPosition;         // total distance
var timeIncrement = duration / iterations;
var position      = 0;
var time          = 0;

function move() {
    time += timeIncrement;
    position = linear(time, startPosition, distance, duration);
    if (position >= endPosition) {
        clearInterval(handler);
        box.style.left = endPosition + "px";
        return;
    }
    box.style.left = position + "px";
}

var handler = setInterval(move, 1000/fps);

function linear(time, begin, change, duration) {
    return change * (time / duration) + begin;
}
body {
    background: gainsboro;
}
#box {
    width: 100px;
    height: 100px;
    background: white;
    box-shadow: 1px 1px 1px rgba(0,0,0,.2);
    position: absolute;
    left: 0;
}
<div id="box"></div>


The parth that needs attention in this code is:

var timeIncrement = duration / iterations;
var time = 0;

function move() {
    time += timeIncrement;
    position = linear(time, startPosition, distance, duration);
    // ...

In the first animation, we directly modified the position variable. We needed a constant position increment value. The way we calculated that is posIncrement = distance / iterations. With easing, we no longer modify the position variable, but the time variable. So we need a time increment value. We calculate it the same way we did position increment, only this time we divide duration by iterations. We increase the time with time increment and pass the time to easing function, and easing function returns us the next position the box should occupy.

total distance / iterations (frames) = position change per frame
total duration / iterations (frames) = time change per frame

Here are some graph for the eye.

Ease function graph


And lastly, an easeInOutQuad example.

JSFiddle

var box = document.getElementById("box");

var fps           = 60;
var duration      = 2;                                   // seconds
var iterations    = fps * duration;                      // 120 frames
var startPosition = 0;                                   // left end of the screen
var endPosition   = window.innerWidth - box.clientWidth; // right end of the screen
var distance      = endPosition - startPosition;         // total distance
var timeIncrement = duration / iterations;
var time          = 0;
var position      = 0;

function move() {
  time += timeIncrement;
  position = easeInOutQuad(time, startPosition, distance, duration);
  if (position >= endPosition) {
    clearInterval(handler);
    box.style.left = endPosition + "px";
    return;
  }
  box.style.left = position + "px";
}

var handler = setInterval(move, 1000 / fps);

function easeInOutQuad(t, b, c, d) {
  if ((t /= d / 2) < 1) {
    return c / 2 * t * t + b;
  } else {
    return -c / 2 * ((--t) * (t - 2) - 1) + b;
  }
}
body {
    background: gainsboro;
}
#box {
    width: 100px;
    height: 100px;
    background: white;
    box-shadow: 1px 1px 1px rgba(0,0,0,.2);
    position: absolute;
    left: 0;
}
<div id="box"></div>