During the past few months, I’ve been actively teaching myself how to draw and animate SVG shapes. I’ve been using CSS transitions, as well as tools like D3.js, react-motion and GSAP, to create my animations.
One thing about animations in general and the documentation these and other animation tools recommend is using easing functions. I’ve been working with them in some capacity over the years, but to be honest, I would never know which function to choose for which kind of animation. Moreover, I did not know about the magic that goes into each of these functions, the notable differences between them, and how to use them effectively. But I was fine with that because I knew that easing functions somehow “smoothed” things out and mostly made my work look realistic.
Here, I present to you what I learned about easing functions in the form of a primer that I hope gives you a good understanding as you dig into animations.
How I got into easing functions
I tried to re-create a pattern called rotating snakes, an optical illusion that tricks the brains into thinking that circles rotate and “dance” when they are not.
I quickly found a gap in my knowledge when trying to build this out. It’s hard! But in the process, I discovered that easing functions play a big role in it.
I turned to JavaScript to draw a bunch of concentric circles in SVG using a library:
for (i = 1; i <= 10; i++) {
drawCircle({radius: i * 10});
}
This was the result:
But that clearly does not look anything like the picture.
As I thought things through, I realized that I was looking for a certain property. I wanted the change in radius of the concentric circles to be small at the beginning and then become larger as the radius increases.
This means that the linear increase in radius using i++
won’t do the trick. We need a better formula to derive the radius. So, my next attempt looked something like this:
let i = 1;
let radiusList = [];
let radius = 0;
while (i <= 10) {
drawCircle({radius: i * 10});
if(i < 4) { i = i + 0.5 } else { i = i + 1 }
}
…which got me this:
Hmm, still not what I wanted. In fact, this deviates even further from the pattern. Plus, this code is hardly customizable unwieldy to maintain.
So, I turned to math for one last attempt.
What we need is a function that changes the radius organically and exponentially. I had an “Aha!” moment and maybe you already see it, too. Easing functions will do this!
The radius of each circle should increase slowly at first, then quickly as the circles go outward. With easing, we can make move things along a curve that can slow and speed up at certain points.
A quick Google search landed me at this gist which is a well-documents list of easing functions and really saved my day. Each function takes one input value, runs formulae. and provides an output value. The input value has to be between 0 and 1. (We will dig into this reasoning later.)
A quadratic easing function looked promising because all it does is square the value it receives:
function (t) { return t*t }
Here’s the code I wound up using:
const easing = (t) => {
return t*t
}
for(i = 0; i<=1; i=i+0.05) {
const r = easing(i) * 40;
drawCircle(r);
}
And we have a winner!
The difference between this pattern and my first two attempts was night and day. Yay for easing functions!
This little experience got me really interested in what else easing functions could do. I scoured the internet for cool information. I found old articles, mostly related to Flash and ActionScript which had demos showing different line graphs.
That’s all pretty outdated, so here’s my little primer on easing functions.
What are easing functions?
They’re a type of function that takes a numeric input between 0 and 1. That number runs through the specified function and returns another number between 0 and 1. A value between 0-1 multiplied by another value between 0-1 always results in a value between 0-1. This special property helps us make any computation we want while remaining within specific bounds.
The purpose of an easing function is to get non-linear values from linear value inputs.
This is the crux of what we need to know about easing functions. The explanations and demos here on out are all geared towards driving home this concept.
Easing functions are a manifestation of the interpolation concept in mathematics. Interpolation is the process of finding the set of points that lie on a curve. Easing functions are essentially drawing a curve from point 0 to point 1 by interpolating (computing) different sets of points along the way.
Robert Penner was the first to define easing functions and create formulae for different ones in his book.
The five types of easing functions
There are five types of easing functions. They can be mixed, inverted and even mashed together to form additional, more complex functions. Let’s dig into each one.
Linear easing functions
This is the most basic form of easing. If the interval between the points we interpolate between 0 and 1 are constant, then we then form a linear easing function.
Going back to the concentric circles example earlier, increasing the radius of the initial circle by a constant amount (10px in that example) makes a linear function.
It should come as no surprise that linear is the default easing function. They’re extremely simple because there is no curve to the animation and the object moves in a straight, consistent direction. That said, linear functions have their drawbacks. For example, linear animations tend to feel unnatural or even robotic because real-life objects rarely move with such perfect, straight motion.
Quadratic easing functions
A quadratic easing function is created by multiplying a value between 0 and 1 by itself (e.g. 0.5*0.5). As we learned earlier, we see that this results in a value that is also between 0 and 1 (e.g. 0.5*0.5 = 0.25).
To demonstrate, let’s make 10 values between 0 and 1 with a quadratic function.
const quad_easing = (t) => t*t;
let easing_vals = [];
for(let i = 0; i < 1; i +=0.1) {
easing_vals.push(quad_easing(i));
}
Here’s a table of all the values we get:
Input Value (x-axis) | Quadratic Eased Value (y-axis) |
---|---|
0 | 0 |
0.1 | 0.01 |
0.2 | 0.04 |
0.3 | 0.09 |
0.4 | 0.16 |
0.5 | 0.25 |
0.6 | 0.36 |
0.7 | 0.49 |
0.8 | 0.64 |
0.9 | 0.81 |
1 | 1 |
If we were to plot this value on a graph with x-axis as the original value and y-axis as the eased value, we would get something like this:
Notice something? The curve is practically the same as the ease-in functions we commonly find, even in CSS!
Cubic, Quartic and Quintic easing functions
The final three types of easing functions behave the same, but work with a different value.
A cubic easing function is creating by multiplying a value between 0 and 1 by itself three times. In other words, it’s some value (e.g. t), cubed (e.g. t3).
Quartic functions do the same, but to the power of 4. So, if t is our value, we’re looking at t4
And, as you have already guessed, a quintic function runs to the power of 5.
The following demo will give you a way to play around with the five types of functions for a good visual of how they differ from one another.
See the Pen Plotting Easing functions by Pavithra Kodmad (@pkodmad) on CodePen.
Easing in and easing out…or both!
“An ease-in-out is a delicious half-and-half combination, like a vanilla-chocolate swirl ice cream cone.”
— Robert Penner
Ease in and ease out might be the most familiar easing animations. They often smooth out a typical linear line by slowing down at the start or end (or both!) of an animation.
Ease-in and ease-out animations can be created using any of the non-linear functions we’ve already looked at, though cubic functions are most commonly used. In fact, the CSS animation
property comes with ease-in
and ease-out
values right out of the box, via the animation-timing-function
sub-property.
ease-in
: This function starts slow but ends faster.ease-out
: This function starts fast and ends slower.ease-in-out
: This function acts as a combination of the others, starting fast, slowing down in the middle, then ending faster.
See the Pen Easing demo by Pavithra Kodmad (@pkodmad) on CodePen.
Go ahead and play around with them on this cubic-bezier.com.
These curves can be created in JavaScript as well. I personally like and use the bezier-easing library for it. Easing.js is another good one, as is D3’s library (with a nice example from Mike Bostock). And, if jQuery is more your thing, check out this plugin or even this one.
See, it’s pretty “ease”-y!
I hope this little primer helps illustrate easing functions and interpolation in general. There are so many ways these functions can make animations more natural and life-like. Have a look at Easing.css for a UI that allows you to create custom curves and comes with a slew of preset options.
I hope the next time you use an easing function, it won’t be a blackbox to you. You now have a baseline understanding to put easing functions to use and open up a ton of possibilities when working with animations.
More on easing
We’ve only scratched the surface of easing functions here, but there are other good resources right here on CSS-Tricks worth checking out to level up even further.
Easing is so much fun! GSAP has quite a few unique easing-related tools that might be worth exploring:
Make an ease using an SVG path itself! Literally draw/drag in the browser, unlimited points. https://greensock.com/customease
Wiggle and bounce (with squash and stretch): https://greensock.com/wiggle-bounce
Zooming into or out of things smoothly presents a rather unique challenge in terms of easing: https://greensock.com/docs/Easing/ExpoScaleEase
Thanks for writing the article! Well done.
Stared at the rotating snakes trying to make them stop.
Couldn’t.
Read the article with bleeding eyes :-D
Not my smartest move.