Easing Functions (aka Timing Functions) in CSS3

by kirupa   |   3 April 2013

When you are creating an animation (or transition), what happens to your animated properties while your animation is running is almost as important as what happens at the end. For example, your property values could change linearly with time:

boring, linear ease

In this case, what you see is an animation that neither seems to speed up nor slow down. Your animation moves at a steady, constant, and (possibly) boring rate. You can kick things up a notch by having your properties change with a little more style:

more exciting, easing out function

In this example, your property changes very quickly at the beginning and then slows off towards the end.

In both of these cases, the end result is the same. At the beginning, your property has a value. At the end, after whatever duration is specified elapses, your property has a final value. If you closed your eyes just when the animation started and opened them once the animation completed, you'll have no idea which of the two animation variants actually played. The only thing that is different between the linear case and the non-linear case is the speed at which the property values changed at different times in the animation's life.

There is a name for these speed variations that alter how properties animate from their initial value to their final value. That name is easing function, and this tutorial is all over it.

Onwards!

Making Sense of Easing Functions

In the previous section, you saw two colorful graphs that visualized how properties change in the presence of an easing function. In one you had a linear ease that didn't do much. In the other, you had a non-linear ease that slowed your property changes down as your animation was running. In this section, let's turn the camera around a bit and focus exclusively on easing functions without getting distracted by actual property values and durations. As part of this look, you will see some sweet diagrams containing lines, labels, numbers, and other things that you may not have seen since school. Yippee!

Meet the Easing Function Curve

Whenever anybody talks about easing functions, it's only matter of time before a graph of what is known as the easing function curve is drawn:

this is what I want

Knowing how to draw and understand this curve is an important part of mastering easing functions, so let's look at how to define this curve. To help with all this, we need a patsy. We need an example.

The Example

Our example is going to be pretty simple. What we have is an element whose opacity property value linearly changed from 1 to 0 over a period of 2 seconds with a linear ease applied. This could either be an animation or a transition - it really doesn't matter.

A chart of this example where we plot the opacity value and duration would look as follows:

the opacity go down the hole

From looking at this chart, it is pretty easy to figure out what the value of your opacity property would be at any given point during the 2 second animation. At the 1 second mark, your opacity property would have a value of .5. At the 1.5 second mark, your opacity property would be .25, and so on.

Visualizing Easing Functions

Here is where things get interesting. Easing functions define the rate at which your property changes. What a property value is at any given time isn't nearly as important as how that property changed from its initial value to the final value over the lifetime of the animation. This means that all of the earlier charts I've provided are no good. To draw a chart through the eyes of an easing function, let's generalize things a bit and switch over to using percentages.

Instead of plotting the property value over a period of time, let's plot a ratio of both of them instead. Let's plot the percentage of how much progress the property has made to reach its final value compared to how much of the animation has been completed. Our earlier chart transformed to take into account these new expectations will look as follows:

percentage

While our graph looks different, the details that you had before earlier is still represented here. You just have to dig a little bit deeper. In our example, the opacity property goes from starting at 1 to ending at 0. At the beginning with 0% of your animation having been completed, your opacity property is 0% of the way there to reaching its final value of 0:

at the beginning

That is why our blue line starts of at 0% for your property progression.

When your animation completes, your opacity property is at 0...aka the final value. Another way of saying that is that it reached 100% of where it needed to go, and it did that right at the end with 100% of your animation having been completed:

in the end, it doesn't even matter!

As you can see, our blue line ends at 100% for both completion as well as property progression. Because the change is linear, what you get is a straight line from the (0%, 0%) point at the bottom-left to the (100%, 100%) point at the top-right.

With this alternate representation for looking at your animation, the thing to note is that it no longer matters what the property value you care about actually is at the beginning or the end. The value could be something between 0 and 1 for opacity; something between #FFFFF and #00000 for a color; something positive and negative; and a whole lot more. It also no longer matters what the duration is. Your animation being .2 seconds long or 600 seconds long are no longer important. At this point, we've pretty much generalized all the pesky details away into the simple ratio between progress and completion. All that matters is what percentage of the final property value has been reached at any given point during the animation's lifetime.

Visualizing Easing Functions...for Real This Time!

This is far less dramatic than what this section heading may imply. The percentage-based graphs you saw in the previous section contain the easing function. I just didn't highlight it because the timing wasn't right:

the easing function

The blue line is the mythical easing function curve. Now that you know this, let's try to understand this curve more.

Linear Cases

For a linear ease, as you have seen so far, the end result is a straight line. Your animation's completion and how far the property has progressed move hand-in-hand:

more progress

To highlight this, the arbitrarily chosen 30% mark represents both how far the property has progressed as well as how much of the animation has run to completion. Mapping this to what you will see on your screen, because the property is changing from 1 to 0 over 2 seconds, 30% represents an opacity value of .7 and an elapsed time of .6 seconds. This is just some simple multiplication.

For the most part, though, you will never actually need to do any simple multiplication to translate from this percentage based world to the actual property values. All you really need to know from looking at the easing function curve is how it will affect your animation. With a straight line like this, it is very clear how your final animation will be affected.

The Awesome Non-Linear Cases

Of course, not everything is as simple as what you have with a linear easing function. For the more exotic non-linear cases, how far your property has progressed will diverge from how much of your animation has actually been completed:

non-linear ease

For example, let's take a look at the 75% completion mark and see where things stand:

a look at the non-linear cases

Eyeballing things from this graph, your animation completing 75% of its life doesn't mean that the property has reached 75% of its final value. The value is more like 45% percent instead. From looking at the easing function curve, notice that your property changes don't catch up to your animation's completion percentage until the very end. What this means that your animation starts off pretty slow and then speeds up only much later. A different easing function may do something different, and you'll get to see a lot of these different easing functions shortly.

What You Can and Can't Do

We are almost done with the theoretical book learning. The last thing we are going to look at before getting even more serious is a boring overview of what you can and can't do using easing functions in CSS.

You Always Start at 0% and End at 100%

The most important limitation you should know about is that your property progression will always start at 0% in the beginning and end at 100% upon animation completion. It doesn't matter what your easing function does in the middle. The beginning and end are clearly defined and can't be changed:

starting and ending points are the same

What does this mean? This means your easing function can never get your animated properties to start off at anything but their initial values. Likewise, at the end of the animation, your easing function will never get your animated properties to stop at anything but the final value. Between the beginning and the end, the easing function may do all sorts of crazy things that affect the animated properties in similarly crazy ways. It is just that, at the beginning and the end, order is maintained.

There Is No Box

Speaking of crazy things that happen outside of the beginning and the end, your property values can change beyond 100% and below 0%:

going beyond 100

Being able to go beyond the range prescribed by your property's initial value and final value is a very important detail that can help make your animations more realistic! One of the 12 Basic Principles of Animation is called Follow through. Follow through refers to an animation technique where things don't stop animating suddenly. They exceed their final target slightly before snapping back into place. This useful technique is something that can only be done by going beyond the 0% and 100% range.

My Name is Curve...Cubic Bezier Curve!

So far, we've looked at easing functions in a very general, imprecise sense. As part of learning about them, such hand waving is acceptable. Now that we are getting close to actually using easing functions, we need to get a little bit more precise. Let's start with defining our easing function curve more formally.

Our easing function curve isn't simply called an easing function curve. That is simply its stage name. The easing function curves are more formally known as cubic bezier curves. While I won't go into great mathematical detail about cubic bezier curves, I will provide you with just enough information so that you can effectively use them to create awesome animations.

Let's get started by taking a look at the following easing func...err cubic bezier...curve:

simple easing function again

This curve doesn't look the way it does because it fell off the wagon like that. It looks this way because of various precisely placed points that mathematically influence its shape:

the cubic bezier curve in action

The thing to know is that a cubic bezier curve is made up of four points. I've labeled those four points, as seen in the above diagram, as P0, P1, P2, and P3. Each point is made up of two values that represent its horizontal and vertical position in our chart - two values we'll simply call x and y:

x and y values

I described the point values in terms of decimals as opposed to percentages because you'll rarely see percentages use to mark a cubic bezier point. From the above diagram, you can see how I got the values for both P1 and P2 pretty easily. I just translated the values from the completion and property progression axes into decimals. Notice that I didn't even bother labeling P0 and P3 because they are always going to be (0, 0) and (1, 1) respectively in our HTML world.

The point values are extremely important not just because they better explain our charts. They are important because it is these values that you specify in your CSS - something which you will see shortly!

Easing Functions in CSS

Finally, we get to move beyond analyzing graphs and move into CSS territory. The CSS properties that define an easing function are the cleverly named transition-timing-function and animation-timing-function. The names of these properties is a dead giveaway (like a will...booyah!) on when they make an appearance. The animation-timing-function is used with CSS animations, and the transition-timing-function is used with CSS transitions.

Let's quickly look at how these timing functions survive in their respective habitats.

Easing Functions in Animations

In a CSS animation, the animation-timing-function property can be defined in two places. It can be defined as part of the animation declaration:

/* shorthand */
#foo {
	animation: bobble 2s ease-in infinite;
}

/* longhand */
#somethingSomethingDarkSide {
    animation-name: deathstar;
    animation-duration: 25s;
    animation-iteration-count: 1;
    animation-timing-function: ease-out;
}

It can also be declared on each individual keyframe:

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}

When you declare your timing function as part of the animation declaration, what it really means is that each of your keyframes will actually be affected by that timing function value. It is no different than specifying the animation-timing-function on each keyframe individually, but you do save a few characters of typing.

By way of cascading, a timing function declared on a keyframe will override any timing function specified on the animation declaration as well. That is a good thing to know if you want to mix and match timing functions and have them live in different places. One last thing to note is that the animation-timing-function declared in a keyframe only affects the path your animation will take from the keyframe it is declared on until your animation reaches the next keyframe. This means you can't have an animation-timing-function declared on your last keyframe because there is no "next keyframe". If you do end up declaring a timing function on the last keyframe anyway, that timing function will simply be ignored…and your friends and family will probably make fun of you behind your back for it.

Easing Functions in Transitions

Transitions are a bit easier to look at since your transition-timing-function property can only live as part of the transition declaration:

/* shorthand */
#bar {
	transition: transform .5s ease-in;
}

/* longhand */
#karmaKramer {
	transition-property: all;
	transition-duration: .5s;
	transition-timing-function: ease-in;
	transition-delay: .1s;
}

To be fair, with a transition, there is nowhere else any transition property could live, so don't heap too much praise on it.

Note - Default Timing Function Values

Specifying a timing function as part of your animation or transition is optional. The reason is that every animation or transition you use has its timing-function property defined by default with a value of ease.

Meet the Easing Functions / Timing Functions

Now that you have an idea of how easing functions fit into the whole CSS world, let's actually look at the various easing functions themselves. The easing functions you can set for the transition-timing-function and animation-timing-function properties are:

In the following sections, I will go through and very briefly explain more about each one...sort of.

cubic-bezier()

Let's start with the most general-purpose of the easing functions - the cubic-bezier() one. This function takes four numbers as its argument, and these numbers map to points P1 and P2 from your easing function curve:

x and y values

The first two arguments are made up of the x and y positions of point P1. The second two arguments are made up of the x and y positions of point P2:

definition time

Points P0 and P3 are ignored because they are always going to be the same. If you recall from earlier, your properties will always start at their initial value and always end at their final value. By not being able to specify them at all, you kinda ensure that is the case.

When you put those numbers in, your cubic-bezier easing function will look as follows:

.foo {
	transition: transform .5s cubic-bezier(.70, .35, .41, .78);
}

Now, for all practical purposes, you do not want to be randomly entering values into your cubic-bezier function and testing to see if the final result is what you like. That is just an awful use of time. What you want to do is visit the handful of online resources that simplify this task immensely.

My favorite of those online resources is Lea Verou's cubic-bezier generator:

lea verou's cubic-bezier.com 

Her site allows you to do everything you need in order to end up with a usable easing function. You can play with the cubic bezier curves, preview what an animation using that easing function would look like, and easily get the values for your cubic-bezier function for use in your CSS.

My next favorite site is by Matthew Lein and his CSS Easing Animation Tool:

easing experiments and visualizations

You can easily preview and play with not only the built-in CSS easing functions but also get the values for all of the Robert Penner easing equations as well!

Between Lea and Matthew's sites, you should never have to painfully try to fiddle with the cubic bezier point values outside of just entering them.

The Other Easing Functions

By knowing how to define the cubic-bezier() function, you can create any sort of ease imaginable. The only downside with this function is that you have to specify the four values that make up the curve. The two web sites I posted certainly help with this, but so do the built-in easing functions you can specify such as ease, linear, ease-in, ease-out, and ease-in-out.

These built-in functions are provided simply as a shortcut. You can re-create them by entering the correct point values into the cubic-bezier function. With that said, shortcuts are awesome. Here is what the cubic bezier curves for all of these would look like:

all of the eases

In the above diagram, I gave our ease easing function some extra real estate both to make the layout work but also as a sign of respect because it is the default ease you get if you don't explicitly set your transition-timing-function and animation-timing-function.

The step function

The last thing we will look at is something that affects the rate at which your properties change but isn't an easing function. This non-easing function creature is known as a step function:

the step function

What a step function does is pretty unique. It allows you to play back your animation in fixed intervals. For example, in the step function graph you see above, your animated property ratio starts at 0%. At the 50% mark, it jumps to 50%. At the end of the animation, your property ratio reaches 100%. There is no smooth transition between the various frames or "steps". The end result is something a bit jagged.

In CSS, the step function can be defined by using the appropriately named steps function:

.pictureContainer img {
	position: relative;
	top: 0px;
	transition: top 1s steps(2, start);
}

The steps function takes two arguments:

  1. Number of steps
  2. A value of start or end to specify whether the first step should occur at the beginning of the animation or whether the last step occurs when the animation ends

For example, if I want my animation to have five steps and have a step when the animation ends, my steps function declaration would look as follows:

.pictureContainer img {
	position: relative;
	top: 0px;
	transition: top 1s steps(5, end);
}

One thing to note is that, the more steps you specify, the smoother your animation will be. After all, think of an individual step as a frame of your animation. The more frames you have over the same duration, the smoother your final result will be. That same statement applies for steps as well.

Further Reading

Given the prevalence of easing in both traditional and computer generated animations, there is a lot of good stuff out there that you can read for more details. I've picked just a handful of my favorites:

Need Help?

If you have questions, need some assistance on this topic, or just want to chat - post in the comments below or drop by our friendly forums (where you have a lot more formatting options) and post your question. There are a lot of knowledgeable and witty people who would be happy to help you out. Plus, we have a large collection of smileys you can use

Share

Did you enjoy reading this and found it useful? If so, please share it with your friends:

If you didn't like it, I always like to hear how I can do better next time. Please feel free to contact me directly at kirupa[at]kirupa.com.

Cheers!

Kirupa Chinnathambi

 

Add Your Comment (or post on the Forums)

blog comments powered by Disqus

Creating high-quality content is a team effort that takes a boatload of time. If you found what you see here helpful, please consider sending a small tip:

While tipping is entirely optional, we'll be your bestest friend forever if you do.

More Details & Options