Tag: Easing

Ease-y Breezy: A Primer on Easing Functions

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.

The post Ease-y Breezy: A Primer on Easing Functions appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

Reversing an Easing Curve

Let’s take a look at a carousel I worked on where items slide in and out of view with CSS animations. To get each item to slide in and out of view nicely I used a cubic-bezier for the animation-timing-function property, instead of using a standard easing keyword.

See the Pen Carousel with reversed easing curve by Michelle Barker (@michellebarker) on CodePen.

A cubic-bezier can appear confusing at first glance, but when used correctly, it can add a nice touch to the user experience.

While building this carousel, I realized I not only needed a custom animation curve, but had to use it in reverse as well to get the effect right. I figured it was worth sharing what I learned because creating a custom curve and reversing it may seem tricky, but it’s actually pretty straightforward.

First, a primer on easing

Easing is the word used to describe the acceleration and deceleration of an animation’s progress along a timeline. We can plot this as a graph, where x is time and y is the animation’s progress. The graph for a linear animation, which has no acceleration or deceleration (it moves at the same speed all the way through), is a straight line:

Linear easing graph

Non-linear easing is what gives animations a more natural, life-like feel. We can apply easing to transitions and animations in CSS. The animation-timing-function property allows us to define the easing on an animation. (The same options are available for transition-timing-function.) We have four keywords to choose from:

  • linear – as described above
  • ease-in – the animation starts off slow and accelerates as it progresses
  • ease-out – the animation starts off fast and decelerates towards the end
  • ease-in-out – the animation starts off slowly, accelerates in the middle and slows down towards the end
  • ease – the default value, a variant on ease-in-out
  • Getting to know cubic-bezier

    If none of those options suit our animation, we can create a custom easing curve using the cubic-bezier function. Here’s an example:

    .my-element {   animation-name: slide;   animation-duration: 3s;   animation-timing-function: cubic-bezier(0.45, 0.25, 0.60, 0.95); }

    If we prefer, we can write these properties as shorthand, like this:

    .my-element {   animation: slide 3s cubic-bezier(0.45, 0.25, 0.60, 0.95); }

    You’ll notice the cubic-bezier function takes four values. These are the two pairs of coordinates needed in order to plot our curve onto the graph. What do those coordinates represent? Well, if you’ve used vector illustration programs, like Illustrator, the idea of vector points and “handles” that control the size and direction of the curve might be familiar to you. This is essentially what we’re plotting with a cubic-bezier curve.

    We don’t need to know about all the maths behind cubic-bezier curves in order to create a nice animation. Luckily there are plenty of online tools, like cubic-bezier.com by Lea Verou, that allow us to visualize an easing curve and copy the values. This is what I did for the above easing curve, which looks like this:

    Cubic-bezier easing graph

    Here, we can see the two points we need to plot, where the cubic-bezier function is cubic-bezier(x1, y1, x2, y2).

    Cubic-bezier easing graph showing co-ordinates

    Applying easing in two directions

    My carousel rotates in both directions — if you click the left arrow, the current item slides out of view to the right, and the next item slides in from the left; and if you click the right arrow, the reverse happens. For the items to slide into or out of view, a class is added to each item, depending on whether the user has clicked the “next” or “previous” button. My initial assumption was that I could simply reverse the animation-direction for items sliding out in the opposite direction, like this:

    .my-element--reversed {   animation: slide 3s cubic-bezier(0.45, 0.25, 0.60, 0.95) reverse; }

    There’s just one problem: reversing the animation also reversed the easing curve! So, now my animation looks great in one direction, but is completely off in the other direction. Oh no!

    In this demo, the first box shows the initial animation (an item sliding left-to-right) and the second box shows what happens when the animation-direction is reversed.

    See the Pen Reverse animation by Michelle Barker (@michellebarker) on CodePen.

    You can see the two animations don’t have the same feel at all. The first box speeds up early on and slows down gently as it progresses, while the second box starts off quite sluggish, then gains a burst of speed before abruptly slowing down. This wouldn’t feel right at all for a carousel.

    We have a choice between two options to achieve this:

    1. We could create a new keyframe animation to animate the items out, and then apply the same easing as before. That’s not too hard in this example, but what if we have a more complex animation? It would take quite a bit more work to create the same animation in reverse, and we could easily make a mistake.
    2. We could use the same keyframe animation (with the animation-direction: reverse) and invert the easing curve so we get the same easing in both directions. This isn’t too hard to do, either.

    To reverse the easing curve for our reversed animation we need to rotate the curve 180 degrees on its axis and find the new coordinates.

    Comparing original easing graph with reversed graph

    We can do this with some simple maths — by switching the coordinate pairs and subtracting each value from 1.

    To visualize this, imagine that our original values are:

    x1, y1, x2, y2

    Our reversed values will be:

    (1 - x2), (1 - y2), (1 - x1), (1 - y1)

    In this demo, the third box shows what we want to happen: the item sliding in the opposite direction, but with the easing reversed to give it the same feel.

    See the Pen CSS Variables to reverse easing by Michelle Barker (@michellebarker) on CodePen.

    Let’s walk through how we can calculate the reversed easing curve.

    Calculating the new curve with CSS variables

    We can use CSS variables to calculate the new curve for us! Lets assign each value to a variable:

    :root {   --x1: 0.45;   --y1: 0.25;   --x2: 0.6;   --y2: 0.95;      --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2)); }

    Then we can use those variables to calculate the new values:

    :root {   --reversedCurve: cubic-bezier(calc(1 - var(--x2)), calc(1 - var(--y2)), calc(1 - var(--x1)), calc(1 - var(--y1))); }

    Now, if we make any changes to the first set of variables, the reversed curve will be calculated automatically. To make this a bit easier to scan when examining and debugging our code, I like to break these new values out into their own variables:

    :root {   /* Original values */   --x1: 0.45;   --y1: 0.25;   --x2: 0.6;   --y2: 0.95;      --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2));      /* Reversed values */   --x1-r: calc(1 - var(--x2));   --y1-r: calc(1 - var(--y2));   --x2-r: calc(1 - var(--x1));   --y2-r: calc(1 - var(--y1));      --reversedCurve: cubic-bezier(var(--x1-r), var(--y1-r), var(--x2-r), var(--y2-r)); }

    Now all that remains is to apply the new curve to our reversed animation:

    .my-element--reversed {   animation: slide 3s var(--reversedCurve) reverse; }

    To help visualize this, I’ve built a little tool to calculate the reversed values of a cubic-bezier. Enter the original coordinate values to get the reversed curve:

    See the Pen Reverse cubic-bezier by Michelle Barker (@michellebarker) on CodePen.

    The post Reversing an Easing Curve appeared first on CSS-Tricks.

    CSS-Tricks

, ,
[Top]