Transiciones CSS
Tags: web, animation, css, transitions
Published: 2020-09-17 17:22:45

---
title: Transiciones CSS
published: true
description: Las Transiciones CSS permiten que los cambios de valor de una propiedad CSS se realicen progresivamente durante un período de tiempo especificado.
tags: web, animation, css, transitions
canonical_url: https://joanleon.dev/transiciones-css
//cover_image: https://direct_url_to_image.jpg
---


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

- [Animación Web](https://dev.to/nucliweb/animacion-web-490k)
- **Transiciones CSS**
- [Animaciones CSS](https://dev.to/nucliweb/animaciones-css-33h9)
- *Animaciones SVG (Próximamanete)*
- *Animaciones JavaScript  (Próximamanete)*
- *Animaciones Canvas  (Próximamanete)*

Aquí encontrarás una explicación teórica y detallada de las **Transiciones CSS**, si quieres ver ejemplos de implementación y casos de uso, echa un vistazo al [vídeo](https://youtu.be/iRAVt8gsIbM).

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

### Cambios de estado de propiedades CSS

Normalmente, cuando cambiamos el valor de una o varias propiedades CSS, el resultado representado por el navegador se actualiza instantáneamente. Lo podemos ver en el siguiente ejemplo, donde los valores de `width` y `box-shadow` cambian inmediatamente al poner el cursor sobre el logo, o un tap si estás en un dispositivo táctil.

{% codepen https://codepen.io/claireparker/pen/ExjxdzL %}

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

Las transiciones son un efecto de presentación. El motor de renderizado del navegador hace una [interpolación](https://es.wikipedia.org/wiki/Interpolaci%C3%B3n), calculando los valores intermedios entre un valor y otro.

Aquí tenemos el mismo ejemplo aplicando la transición.

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

{% codepen https://codepen.io/claireparker/pen/OJVJBeV %}

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

### Así funciona la transición

Si tenemos un elemento en nuestra página que queremos desplazar 200px a la derecha, lo podemos hacer con la propiedad `transform`.

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

> En el ejemplo usaremos un poco de Javascript para añadir la interacción, en este caso añadiremos una clase para indicarle al elemento su nuevo estado. _Podríamos hacer esto mismo sin Javascript, con un checkbox y combinación de selectores CSS, pero yo creo que Javascript nos sirve para añadir la interacción con las usuarias. Pero si hay interés ya veremos en un artículo de CSS cómo hacer este tipo de cosas sin Javascript._

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

{% codepen https://codepen.io/claireparker/pen/abOOKeL %}

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

Como hemos visto, la propiedad `transition` indica al navegador que haga la interpolación. El navegador intentará, según los recursos disponibles, actualizar el renderizado de la página a 60 fotogramas por segundo.

Si tenemos en cuenta que el desplazamiento es de 200px en un tiempo de 0.5 segundos, en un caso ideal de 60 fotogramas/segundo, la interpolación será de 30 fotogramas. El navegador hará el cálculo para la interpolación: `200px/30fps = 6.666px`, así que desplazará el elemento 6.666px a la derecha en cada ciclo de la animación.

En la siguiente imagen podemos ver la representación que hace Chrome en el Developer Tools de la transición.

![Developer Tools: Transición](https://joanleon.dev/static/957f4a1dbdabcc8b31917c32d9910812/30744/devtools-transition.png)

Tenemos una representación gráfica del número de fotogramas en la línea de tiempo, y también podemos ver el tiempo que ha tardado en hacer la transición **502,29 ms**.

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

> En las herramientas de desarrollos, tanto Chrome como Firefox, tenemos mucha información y herramientas. Ya lo analizaremos en detalle en un futuro artículo, y conocer cómo las podemos utilizar para depurar nuestras animaciones o analizar las animaciones de otras páginas web (se aprende mucho analizando el desarrollo de otras personas).

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

## Propiedades CSS para las transiciones

Analicemos en detalle cada una de las propiedades CSS que tenemos para definir y controlar las transiciones.

### transition-property

En esta propiedad podemos definir la **propiedad** CSS a la que queremos aplicar la transición.

Si queremos aplicar una transición entre el cambio de color de fondo de un elemento, deberíamos definirlo de la siguiente forma: `transition-property: background-color`.

Si echamos un vistazo a la documentación de la **W3C** veremos la siguiente tabla.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | none OR [ all OR `<property-name>` ]#               |
| **Initial value**  | all                                                 |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

Esta tabla la veremos en casi todas la propiedades CSS en la documentación oficial de la **W3C**, nos informa de los posibles valores que podemos definir para esa propiedad, el valor inicial que tiene si no definimos uno nosotras, a los elementos que aplica, el valor calculado, si hereda el valor del elemento ancestro y si es un valor que se puede animar.

Un valor destacable de esta información es el valor inicial, `all` lo que hace que podamos obviar la definición de la propiedad que queremos animar. Otra información interesante es el hashtag `#` que vemos al final de los valores, esto indica que la propiedad admite una lista de valores separados por comas.

Así que podríamos definir lo siguiente para hacer referencia a varias propiedades.

```css
.logo {
  transition-property: width, box-shadow;
}
```

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

> Dedicaré un artículo a la performance en la animación, y analizaremos si tiene impacto definir las propiedades que queremos animar de forma explícita en lugar de dejar el valor predeterminado `all`.

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

### transition-duration

Con la propiedad `transition-duration` definimos el tiempo de la transición, o transiciones si definimos una lista separada por comas _(aquí también podemos ver el #)_. Acepta valores positivos con unidades de segundos (s) o milisegundos (ms).

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<time>`#                                           |
| **Initial value**  | 0s                                                  |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

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

Si definimos un valor en negativo, el navegador ignora la regla y los cambios se aplicarían sin ninguna transición.

![Negative values](https://joanleon.dev/static/eefabaac5bd61f5de60b60a7905813aa/4caf9/negative.png)

### transition-timing-function

Con la propiedad `transition-timing-function` tenemos la opción de definir las funciones de tiempo. Se trata de una serie de valores: ease, linear, ease-in, ease-out, ease-in-out, step-start, step-end, steps(n, start) predefinidos de funciona de [Curvas de Bécier](https://es.wikipedia.org/wiki/Curva_de_B%C3%A9zier) `cubic-bezier(x1, y1, x2, y2)`.

> Las Curvas de Bézier merecen un artículo específico y detallado, ya que tenemos una especificación dedicada a su definición, [CSS Easing Functions Leve 1](https://www.w3.org/TR/css-easing-1/), así que lo trataremos en futuros artículos analizando los aspectos técnicos y casos de uso de su implementación.

Aquí tenemos la tabla con la información básica.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<timing-function>`#                                |
| **Initial value**  | ease                                                |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

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

Podemos ver que el valor por defecto es `ease` que equivale a la Curva de Bézier `cubic-bezier(0.25, 0.1, 0.25, 1)`.

![ease function](https://joanleon.dev/static/1db08aff35dbdb61adbfd89f572290b8/5d4b2/ease.png)

Esta es su representación gráfica. Como ya he comentado, veremos todas con más detalle en un futuro artículo.

### transition-delay

Como su propio nombre indica, la propiedad `transition-delay` nos permite definir un valor para indicar el timpo de espera antes de empezar la transición.

La tabla de valores es idéntica a la de `transition-duration`.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<time>`#                                           |
| **Initial value**  | 0s                                                  |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

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

Esta propiedad sí que permite definir un valor negativo. Por ejemplo, si tenemos un valor de `-250ms` en una transición de una duración de `500ms` la animación tendrá la mitad del recorrido. Y si el valor negativo es superior a su equivalente positivo de `transition-duration` no obtendremos una transición, sino un cambio inmediato entre los dos estados.

Aquí podemos ver un ejemplo de definir un valor negativo en la propiedad `transition-delay`.

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

{% codepen https://codepen.io/claireparker/pen/jOPrvMP %}

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

### transition (shorthand)

Muchas de las propiedades CSS tienen un _shorthand_ (abreviado). Nos permite utilizar una propiedad con múltiples valores, veamos un ejemplo que creo que es mucho más fácil de entender.

```css
/* Transición con múltiples propiedades */
.logo {
  transition-property: width;
  transition-duration: 0.5s;
  transition-timing-function: ease-out;
  transition-delay: 0.25s;
}

/* Transición con shorthand */
.logoJL {
  transition: width 0.5s ease-out 0.25s;
}
```

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

En el código anterior, las dos clases aplicarían la misma transición. Cuando definimos una transición como shorthand en nuestro código, si la inspeccionamos con el DevTools podremos verlo así.

![DevTools: Transición abreviada](https://joanleon.dev/static/e1a9bb019f40fb83cc2ea853af152d00/10c3b/devtools-transition-shothand.png)

Podemos ver que hay un triángulo, nos indica que hay más información disponible en esa propiedad CSS. Si hacemos clic en él, podremos observar que internamente el navegador las interpreta por separado.

![DevTools: Transición abreviada desplegada](https://joanleon.dev/static/ec092c3663ce20e67330f48defd9f3ee/10c3b/devtools-transition-shothand-open.png)

Como en las propiedades anteriores, en `transition` también tenemos la tabla informativa de la especificación.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<single-transition>`#                              |
| **Initial value**  | all 0s ease 0s                                      |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

**&lt;single-transition&gt;** = [ [ none &Vert; &lt;transition-property&gt; ] &Vert; &lt;time&gt; &Vert; &lt;transition-timingfunction&gt; &Vert; &lt;time&gt; ]#

En los ejemplos que hemos ido viendo he estado utilizando la transición como shorthand, ya que es lo más habitual.

## Trabajando con transiciones

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

### Valores por defecto

A la hora de trabajar con transiciones hay que entender cómo el navegador hace la interpretación de la sintaxis.

El valor por defecto de `transition-property` o como valor en el shorthand `transition` es `all`, eso quiere decir que la transición que apliquemos afectará a todas la propiedades (que lo permitan) de la clase CSS donde lo estemos indicando.

```css
.logo {
  transition-property: all;
  transition-duration: 250ms;
  transition-timing-function: ease;
}

.logoJL {
  transition: all 250ms ease;
}
```

En el código anterior las dos clases hacen lo mismo, pero hay un detalle que debemos tener en cuenta. Si miramos cúales son los valores por defecto de cada una de esas propiedades podremos ver que estamos definiendo valores que el navegador ya los tiene por defecto.

```css
.logo {
  transition-duration: 250ms;
}

.logoJL {
  transition: 250ms;
}
```

Este código tendrá exactamente el mismo comportamiento, aunque no le hayamos definido valores a `transition-property` y `transition-timing-function`.

> Por temas de semántica y posterior interpretación del código, ya sea por nosotras mismas en el futuro, o por otra persona, lo habitual es (como mínimo) encontrarnos con el valor de la `transition-property` definido.

### Transiciones múltiples

Ya lo hemos visto desde el primer ejemplo del artículo.

```css
.logo {
  ... transition: width 0.25s, box-shadow 0.25s;
  ...;
}
```

Hemos definido transiciones de forma independiente para `width` y `box-shadow`. En este caso lo podríamos haber hecho con un valor de `all .25s`, tendría el mismo efecto. Pero hacerlo de forma separada nos permite poder definir diferentes tiempos, retardo o funciones de tiempo a cada una de las propiedades.

```css
.logo {
  ... transition-property: width, box-shadow;
  transition-duration: 0.25s, 250ms;
  transition-timing-function: ease, ease-in-out;
  ...;
}

.logoJL {
  ... transition: width 0.25s, box-shadow 0.25s ease-in-out;
  ...;
}
```

> A modo de ejemplo he utilizado las unidades en segundo y mili segundos, para mostrar que pueden convivir sin problemas.

En el caso de que definamos varias propiedades, donde el número de propiedades es mayor al número de valores en la propiedad duración, este se repite.

En el siguiente código los valores de `transition-duration` y `transition-timing-function` se repetirán hasta coincidir con el número de valores de `transition-property`.

```css
.logo {
  ... transition-property: width, box-shadow, color, padding, border-radius;
  transition-duration: 0.25s, 250ms;
  transition-timing-function: ease, ease-in-out;
  ...;
}
```

Quedando así:

```css
.logo {
  ... transition-property: width, box-shadow, color, padding, border-radius;
  transition-duration: 0.25s, 250ms, 0.25s, 250ms, 0.25s;
  transition-timing-function: ease, ease-in-out, ease, ease-in-out, ease;
  ...;
}
```

![CSS Transtions Canonical Order](https://joanleon.dev/static/0271db5f2caf322db107ee8eca447cef/30744/css-transitions-canonical-order.png)

## El orden de los valores

Como la propia documentación de la _W3C_ nos indica, el orden de los valores en la propiedad `transition` es importante. Como hemos visto en las tablas más arriba nos dicen `<single-transition> = [ none | <single-transition-property> ] || <time> || <timing-function> || <time>`. Eso quiere decir que el navegador está esperando: **none** o una **propiedad**, seguido de la duración, la función de tiempo y por último el tiempo de retardo.

He estado probando varias combinaciones, el navegador es lo suficientemente inteligente como para saber corregir un error en el orden.

```css
.logo {
  transition: ease-in opacity 250ms 3s;
}
```

En el código anterior el navegador será capaz de interpretar la transición a pesar de que los valores no estén ordenados siguiendo el estándar. Incluso si el primer valor de tiempo es negativo, interpreta que estamos indicando el valor de `transition.delay` ya que en `transition-duration` no podemos tener un valor negativo. Os invito a que probéis combinaciones para entender cómo funciona.

## ¿Cómo funcionan por dentro las transiciones?

Más allá de saber cómo definir nuestras transiones para poder añadir animaciones e interacciones, podemos ver en los códigos de los navegadores cómo están aplicando lo que podemos ver en las especificaciones de la _W3C_, que de hecho es una documentación para los desarrolladores de los navegadores, todo sea dicho.

Tenemos la suerte de tener disponible el código fuente, de la mayoría de los navegadores, accesible.

- [Chromium](https://github.com/chromium/chromium) el motor utilizado en Google Chrome, Opera y recientemente por el navegador de Microsoft
- [Geko](https://github.com/mozilla/gecko-dev) el motor que utiliza Mozilla para Firefox.
- [Webkit](https://github.com/WebKit/webkit) fue el motor de Chrome y Opera, pero ahora solo lo utiliza Safari.

Es curioso ver código como el [siguiente](https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc#L2819-L2857), donde podemos ver que justamente cómo crea la lista de valores de una transición en modo shorthand `transition`.

```c
const CSSValue* Transition::CSSValueFromComputedStyleInternal(
    const ComputedStyle& style,
    const SVGComputedStyle&,
    const LayoutObject*,
    bool allow_visited_style) const {
  const CSSTransitionData* transition_data = style.Transitions();
  if (transition_data) {
    CSSValueList* transitions_list = CSSValueList::CreateCommaSeparated();
    for (wtf_size_t i = 0; i < transition_data->PropertyList().size(); ++i) {
      CSSValueList* list = CSSValueList::CreateSpaceSeparated();
      list->Append(*ComputedStyleUtils::CreateTransitionPropertyValue(
          transition_data->PropertyList()[i]));
      list->Append(*CSSNumericLiteralValue::Create(
          CSSTimingData::GetRepeated(transition_data->DurationList(), i),
          CSSPrimitiveValue::UnitType::kSeconds));
      list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
          CSSTimingData::GetRepeated(transition_data->TimingFunctionList(), i)
              .get()));
      list->Append(*CSSNumericLiteralValue::Create(
          CSSTimingData::GetRepeated(transition_data->DelayList(), i),
          CSSPrimitiveValue::UnitType::kSeconds));
      transitions_list->Append(*list);
    }
    return transitions_list;
  }

  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
  // transition-property default value.
  list->Append(*CSSIdentifierValue::Create(CSSValueID::kAll));
  list->Append(
      *CSSNumericLiteralValue::Create(CSSTransitionData::InitialDuration(),
                                      CSSPrimitiveValue::UnitType::kSeconds));
  list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
      CSSTransitionData::InitialTimingFunction().get()));
  list->Append(
      *CSSNumericLiteralValue::Create(CSSTransitionData::InitialDelay(),
                                      CSSPrimitiveValue::UnitType::kSeconds));
  return list;
}
```

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

## Conclusiones

Las transiciones fueron el primer paso para tener animaciones en la web, son tan básicas como potentes. Nos permiten definir unas animaciones muy optimizadas. Combinando varias propiedades, tiempos de duración y tiempos de retardo podemos hacer pequeñas animaciones, pero cuando necesitamos hacer animaciones con mas de dos estados ya tenemos que pasar a utilizar las propiedades `animation` junto con `@keyframes`, pero eso lo veremos en el siguiente artículo de esta serie.


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

#### Compartir, feedback y erratas

- Si te ha gustado el artículo, compártelo para llegar a más gente.
- Si tienes feedback que me pueda ayudar a mejorar, siéntete libre de pasarme un [DM](https://twitter.com/nucliweb).
- Si ves alguna errata o hay algo que no planteo bien, también agradezco las correcciones [DM](https://twitter.com/nucliweb).

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

#### Sígueme en...

- **Twitter:** [@nucliweb](https://twitter.com/nucliweb)
- **YouTube:** [JoanLeon](https://www.youtube.com/c/JoanLeon)
- **Instagram:** [@nucliweb](https://www.instagram.com/nucliweb/)
- **GitHub:** [@nucliweb](https://github.com/nucliweb)