
> Este artículo es parte de una serie de artículos dedicados a la **Animación Web**.

- [Animación Web](/animacion-web/)
- [Transiciones CSS](/transiciones-css)
- **Animaciones CSS**
- _Animaciones SVG (Próximamanete)_
- _Animaciones JavaScript (Próximamanete)_
- _Animaciones Canvas (Próximamanete)_

<div style="margin-bottom: 2em" />

#### Animación CSS con @keyframes

En CSS, a parte de poder animar cambios de estados con [Transiciones CSS](/transiciones-css/), tenemos disponible la propiedad `animate` que junto con la regla arroba (at-rule) `@keyframes` nos permite poder definir una animación controlando el tiempo, múltiples estados (keyframes o fotogramas), número de repeticiones, dirección de la animación y la función de tiempo, para conseguir una animación más natural.

Antes de entrar en detalles, analizando cada una de las propiedades y sus valores, veamos un ejemplo de una animación.

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSAnimation: Logo Animation"
  src="https://codepen.io/nucliweb/embed/KKpoYVr?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/KKpoYVr">
    CSSAnimation: Logo Animation
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>

<div style="margin-bottom: 2em" />

### ¿Qué podemos animar en una web?

La propiedad `animation` nos permite animar muchas de las propiedades CSS: color, posición, márgenes, paddings (no puedo llamarle rellenos 😅), opacidad o tamaños. Tanto en la [W3C](https://www.w3.org/), como en la [MDN](https://developer.mozilla.org/) encontraremos una tabla de características de la propiedad. Una de esas características nos informa del [Animation Type](https://drafts.csswg.org/web-animations/#animation-type), que nos indica el tipo de animación que soporta esa propiedad `not animatable`, `discrete`, `by computed value` o `repeatable list`.

En [este enlace](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties) de la MDN encontrarás una lista de las propiedades CSS que se pueden animar. O [este otro](https://parrot-tutorial.com/cssref/css_animatable.html) donde además hay un ejemplo de cada una de ellas.

<div style="margin-bottom: 2em" />

### La propiedad animation

Hay varias propiedades para definir y configurar una animación, como en muchas de las propiedades CSS, lo podemos hacer con shorcut o definiendo cada una de ellas.

Creo que es mejor conocer las propiedades, y qué hacen, que memorizar el orden de los valores... así que vamos a ver todas la propiedades `animation-*`.

```css
.logo {
  animation-name: logoAnimation;
  animation-duration: 2s;
  animation-timing-function: ease-in-out;
  animation-delay: 1s;
  animation-iteration-count: infinite;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-play-state: initial;
}
```

<div style="margin-bottom: 2em" />

#### animation-name

Con esta propiedad definimos el nombre o nombres de la animación o animaciones que queremos aplicar al elemento, este nombre debe coincidir con uno de los `@keyframes` que tengamos definidos, pero en un rato lo vemos.

#### animation-duration

Bueno, no es difícil intuir que con esta propiedad podemos definir la duración de la animación o animaciones, lo podemos hacer indicándola en segundos o milisegundos.

#### animation-timing-function

En mi opinión, esta es la propiedad de animación más interesante. No permite indicar una función de tiempo que se aplicará ajustándose a la duración de la animación. Tenemos unas cuantas funciones predefinidas, que harán que nuestras animaciones tengan un aspecto más natural.

> Creo que esta propiedad merece un artículo y vídeo dedicado para ver todo su potencial, así que hoy solo veremos los aspectos más básicos.

Acepta valores de "keyword" y de funciones:

```css
/* Keyword values */
animation-timing-function: ease;
animation-timing-function: ease-in;
animation-timing-function: ease-out;
animation-timing-function: ease-in-out;
animation-timing-function: linear;
animation-timing-function: step-start;
animation-timing-function: step-end;

/* Function values */
animation-timing-function: cubic-bezier(0.1, 0.7, 1, 0.1);
animation-timing-function: steps(4, end);
```

El valor por defecto es `ease` y se representa así:

![ease timing function](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694433506/joanleon.dev/assets/animaciones-css/ease.png)

El eje horizontal representa el tiempo de la animación y el vertical representa los valores que se están cambiando en la animación.

De esta forma conseguimos una animación más fluida, en lugar de una animación lineal. Pero ojo, que una animación lineal puede tener sentido en algunas ocasiones, pero lo veremos con ejemplos en el capítulo específico de las **[CSS Easing Functions](https://www.w3.org/TR/css-easing-1/)**.

De momento nos quedamos con que esta propiedad nos permite tener control de cómo se va a calcular la línea temporal de la animación.

#### animation-delay

De igual forma que tenemos una propiedad para definir el tiempo de la animación, también podemos definir cuanto tiempo debe pasar hasta que empiece la animación. También podemos definir el tiempo con segundos y milisegundos, eso sí, podemos definir un delay negativo, lo que afecta a la línea de tiempo de la animación.

Si tenemos una animación de 2 segundos y definimos un `animation-delay: -1s` la animación empezará a ejecutarse de forma inmediata y además empezará en el segundo 1.

![animation-delay](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694433506/joanleon.dev/assets/animaciones-css/animation-delay.png)

#### animation-iteration-count

Con `animation-iteration-count` podemos definir el número de veces que se ejecutará la animación, acepta un número de valor positivo o la palabra clave `infinite`, como en el ejemplo, esto hace que la animación se ejecute de forma infinita.

#### animation-direction

Esta propiedad nos permite indicar la dirección de la animación, acepta 4 valores, veamos las definiciones para entender mejor en qué nos puede ayudar esta propiedad.

`normal`
Cada vez que termina un ciclo, la animación se reinicia al estado inicial y comienza desde el principio. Este es el comportamiento por defecto.

`alternate`
La animación, al terminar un ciclo, invierte su dirección. Es decir, los pasos de la animación se ejecutan al revés. Además, las funciones de tiempo también se invierten; por ejemplo una animación ease-in se convierte en una animación con ease-out cuando se reproduce al revés.

`reverse`
Cada ciclo de la animación se reproduce al revés . Cada vez que comienza un ciclo de animación, ésta se posiciona en el estado final y comienza desde ahí.

`alternate-reverse`
Es similar a alternate pero la animación se reproduce al revés. Es decir la animación se posiciona en el estado final, comienza a reproducirse al revés y, cuando llega al inicio vuelve a reproducirse de forma normal hasta llegar al final de la secuencia.

#### animation-fill-mode

`animation-fill-mode` es la propiedad que nos permite definir el estado después de la animación, y también el estado antes de la animación si existiese delay. Por defecto vuelve a su estado inicial, con el valor normal.

![animation-fill-mode](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694433506/joanleon.dev/assets/animaciones-css/animation-fill-mode.jpg)

> Dibujo de [Ángel Corral](https://twitter.com/ancoar)

<div style="margin-bottom: 2em" />

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSAnimation: Logo Animation, fill-mode default"
  src="https://codepen.io/nucliweb/embed/WNryWxw?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/WNryWxw">
    CSSAnimation: Logo Animation, fill-mode default
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>

<div style="margin-bottom: 2em" />

Aquí podemos ver qué pasa si aplicamos un valor de `forwards`.

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSAnimation: Logo Animation, fill-mode forwards"
  src="https://codepen.io/nucliweb/embed/ExPRJgE?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/ExPRJgE">
    CSSAnimation: Logo Animation, fill-mode forwards
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>

<div style="margin-bottom: 2em" />

Otro de los valores interesantes de esta propiedad es `both`, funcionará igual que `forwards` pero tendrá en cuenta el sentido de la animación. ¿Recordáis que con `animation-direction` podemos hacer que la animación sea invertida?, pues este valor nos ayudará a definir el comportamiento que deseemos en nuestras animaciones.

#### animation-play-state

Esta propiedad también es muy interesante, ya que nos permite dotar de interactividad nuestra animación desde CSS, sin utilizar JavaScript. Ya veremos más adelante cómo trabajar las animaciones con JavaScript, la API de animación es genial... bueno no nos despistemos 😊.

Los valores disponibles son:

```css
/* Single animation */
animation-play-state: running;
animation-play-state: paused;

/* Global values */
animation-play-state: inherited;
animation-play-state: initial;
animation-play-state: unset;
```

El valor por defecto es `running`, y podemos hacer una pausa cambiando su estado. Pero mejor veamos un ejemplo:

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSAnimation: Logo Animation, play-state"
  src="https://codepen.io/nucliweb/embed/ZEQRZeX?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/ZEQRZeX">
    CSSAnimation: Logo Animation, play-state
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>

<div style="margin-bottom: 2em" />

Seguro que has visto que no hay ninguna animación, eso es porque he definido un estado inicial de la animación pausada `animation-play-state: paused;`, y con un hover en el `body` cambio el estado de la animación a `running`. Prueba a poner el cursor, **perdón a la gente que esté leyendo esto desde un dispositivo móvil** 😅, en área de **Result**. Verás que la animación se ejecuta y pausa según haces hover o no sobre el body del documento.

### Los shorthands y valores por defecto

#### El shorthands animation

Por norma general cuando veamos un código CSS con una animación veremos algo como esto:

```css
.logo {
  animation: scale 2s infinite ease-in;
}
```

Es la forma abreviada de definir una animación. La especificación define un orden para los parámetros, así se facilita la compatibilidad entre los navegadores a la hora de su implementación.

```css
.logo {
  animation: "animation-name" "animation-duration" "animation-timing-function"
    "animation-delay" "animation-iteration-count" "animation-direction"
    "animation-fill-mode";
}
```

Los pongo uno bajo el otro para mejorar la legibilidad, pero se definen todos en la misma línea y sin separarlos por una coma. Aunque ese es el orden definido por la W3C, si se cambia el orden, los navegadores son lo suficientemente inteligentes para entender cada uno de los valores a qué hace referencia. Una cosa a tener en cuenta es el `duration` y el `delay`, si solo hay un valor de tiempo, el navegador interpretará que es `duration` y si hay dos, el primero será `duration` y el segundo será para definir el `delay`.

#### Los valores por defecto

En los ejemplos que hemos ido viendo, he definido valor a todas las propiedades por temas docentes, pero hemos de tener en cuenta que la ausencia de alguna de esas propiedades se representarán con su valor por defecto.

Por ejemplo, no hace falta definir `animation-timing-function: ease` o `animation-delay: 0s`, ya que son los valores por defecto.

### Los @keyframes

Hasta ahora hemos visto todas las propiedades de tiempo, función, iteraciones, etc... para añadir la animación a una clase, pero, ¿dónde creamos o definimos la animación?

Aquí entran en escena los `@keyframes`. Como vimos en el artículo de las [Transiciones CSS](/transiciones-css/), con las transiciones podemos hacer que el navegador haga una interpolación entre dos estados y conseguir así, una animación. Pero cuando queremos tener una animación con más de 2 estados tenemos que utilizar los `@keyframes`.

![Animación Web](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694433506/joanleon.dev/assets/animaciones-css/animation.png)

Los keyframes hacen referencia a los fotogramas de la animación, y podemos definir los que necesitemos, vamos a ver el códico de la animación que hemos estado utilizando como ejemplo.

```css
@keyframes logoAnimation {
  0% {
    opacity: 0;
    transform: translateX(-100px);
  }
  20%,
  80% {
    opacity: 1;
    transform: translateX(0);
  }
  100% {
    opacity: 0;
    transform: translateX(100px);
  }
}
```

Lo primero que vemos es que no estamos definiendo un selector, sino un [at-rule](https://developer.mozilla.org/es/docs/Web/CSS/At-rule) _(como @media, @support, @font-face, etc...)_, es `@keyframe` segido del nombre que queremos que tenga la animación. Ese nombre es el identificador que estamos utilizando en la propiedad `animation-name`. Seguimos con un bloque, como también hacemos en las clases, pero en esta ocasión no definimos propiedades CSS, definimos bloques de keyframes donde en su interior escibiremos las propiedades CSS que queremos animar... ahora es cuando empieza lo emocionante 😊.

Para definir los keyframes (fotogramas), tenemos dos maneras de hacerlo, mediante palabras clave o con porcentajes.

```css
@keyframes <identifier> {
  [ [ from | to | <percentage> ] [, from | to | <percentage> ]* block ]*
}
```

`from` equivale al **0%** y `to` equivale al **100%**.

Estas dos animaciones son equivalentes, es totalmente indiferente desde el punto de vista del navegador.

```css
@keyframes logoAnimation_keyworks {
  from {
    opacity: 0;
    transform: translateX(-100px);
  }
  to {
    opacity: 1;
    transform: translateX(0px);
  }
}

@keyframes logoAnimation_percentage {
  0% {
    opacity: 0;
    transform: translateX(-100px);
  }
  100% {
    opacity: 1;
    transform: translateX(0px);
  }
}
```

Incluso podemos mezclar las palabras clave y los porcentajes.

```css
@keyframes logoAnimation {
  from {
    opacity: 0;
    transform: translateX(-100px);
  }
  20%,
  80% {
    opacity: 1;
    transform: translateX(0);
  }
  to {
    opacity: 0;
    transform: translateX(100px);
  }
}
```

### Animaciones múltiples

Igual que en las transiciones, la animaciones admiten definir múltiples animaciones. Esto nos permite tener un mayor control si queremos hacer una animación secuencial.

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSAnimation: Logo Animation, multi animation"
  src="https://codepen.io/nucliweb/embed/XWXBJWa?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/XWXBJWa">
    CSSAnimation: Logo Animation, multi animation
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>

<div style="margin-bottom: 2em" />

En este ejemplo he creado una secuencia de 4 animaciones.

```css
.logo {
  animation: fadeIn 1s ease-in forwards, growth 1s 1s ease-in forwards,
    fadeOut 1s 2s ease-out forwards, logoAnimation 500ms 3s ease-in forwards;
}
```

<div style="margin-bottom: 2em" />

<img style="max-width: 100%" alt="Secuencia de animaciones" src="https://res.cloudinary.com/nucliweb/image/upload/f_auto,q_auto/v1594553060/joanleon.dev/assets/animaciones-css/animations.gif" />

Controlamos la secuencia de las animaciones con la propiedad `animation-delay`, sumando el tiempo de las animaciones anteriores.

> Esto es un trabajo muy artesanal y nada escalable, pero eso ya lo veremos cuando trabajemos con las animaciones con JavaScript.

## Conclusión

Hasta aquí hemos visto lo básico de la animación con CSS, en la implementación encontraremos algunos casos donde hay que conocer bien las posibilidades de las propiedades CSS. En futuros capítulos veremos ejemplos de implementación, depuración, optimización y buenas prácticas.
