Curso de CSS / transformaciones y transiciones

Las transformaciones mueven, rotan o escalan la apariencia renderizada de una caja sin recolocar el contenido circundante. Las transiciones animan un cambio entre valores de propiedades, así que selecciona las propiedades y el tiempo de manera deliberada y proporciona una alternativa de movimiento reducido.

Comprende las transformaciones y el flujo normal

El flujo de documentos se refiere a la relación entre donde se coloca originalmente un elemento y los elementos circundantes. Incluso si mueves el botón hacia arriba a translateY(-.5rem), el párrafo de abajo no se mueve hacia arriba de acuerdo con el espacio donde se movió el botón. Los elementos transformados pueden superponerse al contenido circundante o salirse de la pantalla, por lo que dejar suficiente espacio en su ubicación original es una prioridad en lugar de aumentar la distancia de movimiento.

función Ejemplo Resultados a Observar
translate() translate(1rem, -.5rem) Mover 1rem a la derecha, .5rem hacia arriba
rotate() rotate(15deg) Rotar alrededor de un punto de referencia
scale() scale(1.05) Se muestra a 1.05 veces el tamaño original

El hecho de que el tamaño parezca mayor no significa que el espacio ocupado por la caja antes de la conversión sea más grande. Revisa la configuración de Grid o width para cambiar el ancho real de la columna de la tarjeta, o usa transform para crear un efecto visual ligeramente mayor al presionarlo. Las cajas en línea que no se reemplazan, como el texto plano span, no admiten transform, por lo que puedes usar display: inline-block si es necesario. Las definiciones de los objetivos de transformación y el procesamiento de coordenadas se pueden encontrar en el documento de transformación MDN.

Compara el orden de transformación y el origen

Cuando enumeras funciones de transformación, cada función modifica el sistema de coordenadas de las siguientes. Si mantienes fijo el sistema original y calculas puntos, el mismo resultado se obtiene aplicando las funciones de derecha a izquierda. Por lo tanto, traslación/rotación y rotación/traslación generalmente no producen el mismo resultado. En el ejemplo a continuación, la caja rotada de A es visible donde se ha movido hacia la derecha, y la caja de B se ha movido en la dirección afectada por la rotación y es visible más abajo. No te limites a mirar el nombre de la función y asumir que los dos resultados son iguales; compara sus ubicaciones reales.

El centro de rotación y expansión se establece en transform-origin. Si usas left top en lugar del centro predeterminado, girará sobre la esquina superior izquierda. Esto es útil cuando deseas fijar un punto específico, como cuando se abre un menú, pero la superposición con el contenido circundante puede cambiar simplemente cambiando el punto de referencia.

Guarda el siguiente código como transform-compare.html en UTF-8 y ábrelo en tu navegador. No se requieren archivos o bibliotecas externas.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Transform order and origin</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
.comparisons { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); gap: 1rem; }
.slot { padding: 1rem; border: 1px solid #8a9ba7; }
.frame { height: 9rem; display: grid; place-items: center; background: #f0f2f4; }
.box { width: 4rem; height: 2rem; display: grid; place-items: center; background: #166b92; color: white; }
.first { transform: translateX(2rem) rotate(45deg); }
.second { transform: rotate(45deg) translateX(2rem); }
.center { transform: rotate(30deg); transform-origin: center; }
.corner { transform: rotate(30deg); transform-origin: left top; }
  </style>
</head>
<body>
<main>
<h1>The same value also varies depending on the order and reference point</h1>
<div class="comparisons">
  <section class="slot"><h2>Translate, then rotate</h2><div class="frame"><div class="box first">A</div></div></section>
  <section class="slot"><h2>Rotate, then translate</h2><div class="frame"><div class="box second">B</div></div></section>
  <section class="slot"><h2>Center origin</h2><div class="frame"><div class="box center">C</div></div></section>
  <section class="slot"><h2>Top-left origin</h2><div class="frame"><div class="box corner">D</div></div></section>
</div>
</main>
</body>
</html>
  1. Compara las posiciones de A y B. Si cambias el orden de las funciones de .second para que sea igual al de A, las posiciones relativas de las dos cajas serán las mismas. Después de la confirmación, devuélvelo al orden original.
  2. C y D tienen el mismo ángulo de rotación. Cambia solo transform-origin de .corner a center, verifica la diferencia en los puntos de referencia y restaura a left top.
  3. Compara la ampliación cambiando solo la rotación de C a scale(1.2). El área fuera del recuadro crecerá, pero el área circundante no se reubicará. Después de la confirmación, restaúralo a rotate(30deg).

Elige las propiedades y los tiempos de transición

transition no cambia de estado por sí mismo. El cambio comienza solo cuando el valor de CSS cambia al pasar el cursor, enfocar o cambiar de clase. Especifica el nombre de la propiedad a afectar, la duración, la curva de progreso y el retraso de inicio; si se omite el retraso, se predetermina a 0 segundos.

transition: transform 180ms ease 0ms, background-color 180ms ease 0ms;

Aquí, 180ms es 0,18 segundos, y ease es una curva que conecta suavemente el principio y el fin en lugar de proceder solo a la misma velocidad. linear cambia a una velocidad constante. Usa tiempos cortos y sin retrasos primero, luego ajusta según sea necesario. Agregar un retraso largo a un botón aparentemente no responsivo puede dificultar la percepción de los resultados de tus acciones.

Poner transition en la regla base permite que funcione tanto en entrada como en retorno. Una transición declarada solo en :hover desaparece cuando termina el hover y puede producir un retorno diferente. Nombra las propiedades deliberadamente en lugar de usar all. Este ejemplo transiciona transform y background-color; no todas las propiedades se interpolan de la misma manera. Para más detalles, consulta el documento MDN sobre transiciones.

Prueba hover y foco de teclado

Guarda el siguiente código como transition-state.html. Cuando pases el cursor sobre el botón o uses la tecla Tab, el botón se mueve hacia arriba y su color de fondo cambia. El foco tiene además un contorno azul oscuro. El botón es solo con fines de práctica para observar los cambios de estado, y no se realiza ninguna acción adicional al presionar el botón.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Transforms and state transitions exercise</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
.stage { padding: 2rem 1rem; border: 2px dashed #8a9ba7; }
.action { padding: .75rem 1.25rem; border: 1px solid #166b92; border-radius: .4rem;
  color: #183044; background: #eaf4fa; transform: translateY(0);
  transition: transform 180ms ease, background-color 180ms ease; }
.action:hover, .action:focus-visible { transform: translateY(-.5rem); background: #cfe8f4; }
.neighbor { margin-block: 1rem 0; padding: .75rem; background: #f0f2f4; }
@media (prefers-reduced-motion: reduce) {
  .action { transition: none; }
  .action:hover, .action:focus-visible { transform: none; }
}
  </style>
</head>
<body>
<main>
<h1>The paragraph stays in place as the button moves</h1>
<p>Hover over the button or press Tab to focus it.</p>
<div class="stage">
  <button class="action" type="button">Check state</button>
  <p class="neighbor">This paragraph stays in place when the button is transformed.</p>
</div>
</main>
</body>
</html>
  1. Recuerda la posición superior del párrafo a continuación y pasa el cursor sobre el botón. Solo los botones se mueven y los párrafos deben permanecer en el mismo lugar.
  2. Cambia la duración en dos lugares de 180ms a 800ms para observar la entrada y el retorno, luego vuelve al valor original.
  3. Deja el tiempo tal como está y cambia solo la curva de progreso a linear para comparación. Luego restaúrala a ease.
  4. Añade un retraso de 400ms al final de la primera transición. Como la primera transición corresponde a transform, el movimiento empieza más tarde mientras el cambio de color empieza de inmediato. Elimina los retrasos después de la confirmación.
  5. Enfócate en el botón con la tecla Tab y sal con Shift+Tab. Asegúrate de que el contorno de enfoque y el color de fondo describan el estado de funcionamiento.

Reduce el movimiento y verifica los efectos secundarios

Si el usuario lo ha configurado para reducir el movimiento, las reglas del ejemplo para prefers-reduced-motion: reduce eliminan el movimiento, dejando únicamente el color de fondo y las marcas de enfoque. Si simplemente eliminas la transición, el cambio ocurrirá inmediatamente, pero la posición final a la que te moviste permanecerá. En el ejemplo anterior, decidimos que el movimiento decorativo en sí mismo era innecesario, por lo que incluso las transformaciones de hover y focus se cambiaron a none. Consulta la guía de configuración de reducción de movimiento MDN.

Compara los dos estados cambiando la configuración de tu sistema operativo o reproduciendo las características de los medios en el panel Rendering de las herramientas de desarrollador de tu navegador. En la configuración reducida, el botón no debería moverse, solo deberían cambiar el color y el indicador de enfoque. Cuando termines con el laboratorio, restaura la emulación a su estado predeterminado.

Un transform diferente de "none" crea un contexto de apilamiento y puede establecer el bloque contenedor para descendientes absolutos y fijos. Si un elemento emergente o fijo se comporta de manera inesperada, inspecciona a los ancestros transformados. También verifica la superposición, el enfoque del teclado y el movimiento reducido; una transición suave por sí sola no establece un diseño utilizable. Consulta position: relative, absolute, fixed y sticky, z-index y contextos de apilamiento con CSS.