# Advanced CSS Animations with the linear() Easing Function

You can apply animated effects in CSS using:

* [animations](https://developer.mozilla.org/docs/Web/CSS/animation) to animate one or more styles over a period, and
* [transitions](https://developer.mozilla.org/docs/Web/CSS/transition) to animate one or more styles between two states, e.g. hovered and non-hovered.

Each requires an *easing function* to calculate the change in a style over time. The function can vary the speed of change so an element's animation can accelerate and decelerate.

CSS provides built-in and custom [easing functions](https://developer.mozilla.org/docs/Web/CSS/easing-function), but they are intentionally simple. JavaScript was necessary for anything more sophisticated, but a new [`linear()` easing function](https://developer.mozilla.org/docs/Web/CSS/easing-function#linear_easing_function) now permits more natural animations in CSS alone.


## What is an Easing Function?

CSS and many JavaScript libraries define the **animation time T** as a real number starting at 0 and ending at 1. Your actual animation can occur over any period of time. If you're 2.5 seconds into a 5-second animation, T is `2.5s / 5.0s` or `0.5` - it's halfway through.

The **progress P** of an animated property - such as `opacity` or `translate` - is determined by an easing function that is passed time **T** and returns a real number. The function must return 0 when the animation starts (T is `0`) and 1 when the animation ends (T is `1`), but any other value can be returned otherwise (even something outside the 0 - 1 range).

The simplest possible easing function is `linear`, which you can write in JavaScript as:

```js
function linear(T) { return T; }
```

The function sets **progress P** to the same value at **time T**. If T is `0.123` so is P. Plotting this on a graph gives a straight line:

![linear easing function](https://hackmd.io/_uploads/H1HeWhdc0.svg)

You cannot (currently) define functions in CSS so it provides a `linear` keyword to do the same thing:

```css
animation-timing-function: linear;
```

This can be used to animate any CSS property over any duration, e.g. fade-in by animating the `opacity` of an element from 20% to 80% over 2 seconds:

```css
animation-name: fadein;
animation-duration: 2s;
animation-timing-function: linear;

@keyframes fadein {
  0% { opacity: 20%; }
  100% { opacity: 80%; }
}
```

Given a progress value of P (at time T), the value of any animated CSS property is calculated using:

```js
calculatedStyle = P * (endingValue - startingValue) + startingValue
```

The `opacity` example above is therefore calculated as:

```js
calculatedStyle = P * (80% - 20%) + 20%
```

At:

* the start of the linear animation, T is `0` so P is also `0`.

  `opacity` is set to `0 * (80% - 20%) + 20%` which equates to `20%`.

* 1 second in (halfway through the animation), T is `1s / 2s` = `0.5` so P is also `0.5`.

  `opacity` is `0.5 * (80% - 20%) + 20%` = `50%`.

* the end of the animation, T is `1` so P is also `1`.

  `opacity` is set to `1 * (80% - 20%) + 20%` = `80%`.

The browser does all these calculations, so you don't have to! You just need to know that the opacity starts at 20% and ends at 80% over 2 seconds. The `linear` function ensures progress occurs at a consistent rate without speeding up or slowing down.

`linear` easing is a good choice for animating color hues and opacity, but the following example uses it for a 1.5-second `translateY` between `0px` and `300px`:

<iframe height="640" style="width:100%;" scrolling="no" title="animation: linear" src="https://codepen.io/craigbuckler/embed/xxoPRjq?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

[`linear` easing Codepen](https://codepen.io/craigbuckler/pen/xxoPRjq)


## Easing Keywords

The `linear` ball animation above looks unnatural because, in the real world, objects accelerate and decelerate as they move over time.

CSS provides more naturalistic easing keywords named `ease`, `ease-in`, `ease-out`, and `ease-in-out`. For example, `ease-in-out` accelerates over the first third of the duration to reach a maximum speed, then decelerates over the last third:

<iframe height="640" style="width: 100%;" scrolling="no" title="animation: ease-in-out" src="https://codepen.io/craigbuckler/embed/poXdNGJ?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

[`ease-in-out` easing Codepen](https://codepen.io/craigbuckler/pen/poXdNGJ)

Most browser DevTools allow you to examine and change the easing function. Firefox currently has better options than other browsers:

![Firefox DevTools animation editor](https://hackmd.io/_uploads/BkTPHROc0.png)


## Bézier Curve Easing Functions

All CSS-named easing keywords use [Bézier curves](https://developer.mozilla.org/docs/Glossary/Bezier_curve) to define the *shape* of the animation. While this is not as flexible as a JavaScript function, you can define Bézier curves with just two control point coordinates, so they can be expressed in CSS alone. The `ease-in-out` function is identical to:

```css
animation-timing-function: cubic-bezier(0.42, 0.00, 0.58, 1.00);
```

where the first Bézier curve control point is at coordinate `(0.42, 0.00)` and the second is at `(0.58, 1.00)`. Note:

* the first x-axis value of both coordinates must be between 0 and 1. This ensures the curve cannot curl back on itself which would give two or more points at the same time T. A coordinate of `-0.2, 0` or `1.01, 1` is therefore invalid, and no animation will occur.

* the second y-axis value of either coordinate can be any number. It's possible to define curves where the **progress P** falls outside the normal 0 to 1 range. An element moving between 0px and 100px could therefore go to -20px or 150px at some point during the animation before it reaches its final ending state. In other words: *it bounces*.

The following example defines `cubic-bezier(0.7, 1.9, 0.6, 0.4)`:

1. When time T is 0, progress P is 0.
1. When time T is ~0.6, progress P is ~1.0 and the curve reverses.
1. When time T is ~0.8, progress P is ~0.8 and the curve reverses again.
1. When time T is 1, progress P is 1.

The curve approximates to a single bounce:

<iframe height="830" style="width:100%;" scrolling="no" title="animation: cubic-bezier" src="https://codepen.io/craigbuckler/embed/XWLzNLp?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

[`cubic-bezier` easing Codepen](https://codepen.io/craigbuckler/pen/XWLzNLp)

Bézier curves can be difficult to create manually, but you can edit them visually in DevTools or use any of the following online tools:

* [Ceaser CSS Easing Animation](https://matthewlein.com/tools/ceaser)
* [cubic-bezier.com](https://cubic-bezier.com/)
* [easings.net](https://easings.net/)


## Introducing the linear() Easing Function

Bézier curves are flexible, but the example above still feels unnatural:

* The ball decelerates slightly before the first bounce. *Gravity doesn't work that way!*
* In the real world, a dropped ball often bounces more than once before coming to rest.

It's not possible to define a more realistic Bézier curve because you're limited to just two control points. Developers would previously resort to JavaScript to create more sophisticated animations and transitions.

The new `linear()` easing function *(not to be confused with the `linear` named easing keyword)* has been available in all browsers since late 2023. It defines an animation curve as a series of stop points lines. You can add as many stop points as you like to approximate a curve, but 12 points can create an effective, natural-looking three-bounce animation:

<iframe height="660" style="width:100%;" scrolling="no" title="animation: linear() function" src="https://codepen.io/craigbuckler/embed/XWLzpja?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

[`linear()` function easing Codepen](https://codepen.io/craigbuckler/pen/XWLzpja)

Pass stop points to the `linear()` function as a comma-separated list. Each point defines:

1. the animation progress point P (required). This is typically from 0 to 1, but it can go outside those bounds. For example, 1.2 applied to a `translateY` property would move an element 20% beyond its final resting position at some point during the animation.

1. one or two time duration points T (optional). Note that these are from 0% to 100% rather than 0 to 1 as before (possibly so it's easier to identify them from animation points).

The following examples are identical to the `linear` keyword because the animation moves from 0 to 1 over a 0% to 100% duration:

```css
linear(0 0%, 1 100%)
linear(0 0%, 0.5 50%, 1 100%)
```

If you omit the time duration percentage, the browser interpolates the timings. The three animation points below would apply at 0%, 50%, and 100% like above:

```css
linear(0, 0.5, 1)
```

In the following example:

1. The first tenth of the animation occurs in the first 25% of the duration.
1. The animation remains at the first tenth point until the duration reaches 50%.
1. The final 90% of the animation occurs in the last half of the duration.

```css
linear(0, 0.1 25%, 0.1 50%, 1)
```

The following function is identical - notice the two duration points in the same set:

```css
linear(0, 0.1 25% 50%, 1)
```

The ball bounce animation above uses:

```css
linear(0, 0.06, 0.25, 0.56, 1, 0.81, 0.75, 0.81, 1, 0.94, 1 91%, 0.98, 1)
```

This would be difficult to write manually. I copied the code from the [linear generator app](https://linear-easing-generator.netlify.app/), which can convert both JavaScript (perhaps from [easings.net](https://easings.net/)) and SVG curves to their `linear()` function equivalents. The tool also offers options to reduce the number of stop points and round values.

You can also edit `linear()` points in browser DevTools. Again, Firefox has the edge over others:

![Firefox DevTools linear() point editor](https://hackmd.io/_uploads/B1z9wR_50.png)


## Summary

CSS animations and transitions have been available for more than a decade. The techniques are simple to understand, and you won't always need anything beyond easing keywords or Bézier curve timings. The new `linear()` easing function provides a viable option when you require more complex effects and would rather not delve into JavaScript.

For more information, refer to:

* [MDN CSS animations](https://developer.mozilla.org/docs/Web/CSS/animation)
* [MDN `animation-timing-function` property](https://developer.mozilla.org/docs/Web/CSS/animation-timing-function)
* [MDN CSS transitions](https://developer.mozilla.org/docs/Web/CSS/transition)
* [MDN `transition-timing-function` property](https://developer.mozilla.org/docs/Web/CSS/transition-timing-function)
* [MDN easing functions](https://developer.mozilla.org/docs/Web/CSS/easing-function)
* [MDN linear() easing function](https://developer.mozilla.org/docs/Web/CSS/easing-function#linear_easing_function)
* [Ceaser CSS Easing Animation tool](https://matthewlein.com/tools/ceaser)
* [cubic-bezier.com tool](https://cubic-bezier.com/)
* [easings.net tool](https://easings.net/)
* [linear() generator](https://linear-easing-generator.netlify.app/)

