Trazados de movimiento de curva Bezier en CSS puro | trucos CSS

Eres curva de Bézier un amante como yo?

Además de ser elegantes, las curvas de Bézier tienen buenas propiedades matemáticas debido a su definición y construcciónNo es de extrañar que se utilicen ampliamente en tantas áreas:

  • Como herramienta de dibujo/diseño: a menudo se denominan "trazados" en el software de dibujo vectorial.
  • Como formato para representar curvas: Se utilizan en SVG, fuentes y muchos otros formatos gráficos vectoriales.
  • Como función matemática: a menudo se usa para controlar el tiempo de la animación.

Ahora, ¿qué tal usar curvas Bezier como rutas de movimiento con CSS?

Resumen rápido

Dependiendo del contexto, cuando hablamos de una "curva de Bézier", a menudo asumimos una curva de Bézier cúbica 2D.

Tal curva está definida por cuatro puntos:

MarianSigler, dominio público, a través de Wikimedia Commons

Nota: En este artículo generalmente nos referimos a P0 y P3 como puntos finales, P1 y P2 como puntos de control.

La palabra "cúbica" significa que la función base de la curva es un polinomio cúbico. También existen curvas de Bézier "cuadráticas", que son similares pero con un punto de control menos.

El problema

Digamos que le dan una curva de Beiser cúbica 2D aleatoria, ¿cómo animaría un elemento con animación CSS pura para que se mueva? exactamente a lo largo de la curva?

Como ejemplo, ¿cómo recrearías esta animación?

En este artículo, veremos tres métodos con diferentes sabores. Para cada solución, proporcionaremos una demostración interactiva y luego explicaremos cómo funciona. Hay muchas matemáticas y pruebas detrás de escena, pero no se preocupe, no profundizaremos demasiado.

¡Empecemos!

Método 1: Distorsión del tiempo

Aquí está la idea básica:

  • estoy configurando @keyframes para mover el elemento de un punto final de la curva al otro.
  • Warp el tiempo para cada coordenada por separado usando animation-timing-function.

Nota: El artículo de Temani Afif (2021) tiene muchos ejemplos y explicaciones.

Usando cubic-bezier() función con los parámetros adecuados, podemos crear una trayectoria de movimiento en cualquier curva Bezier cúbica:

Esta demostración muestra animación CSS pura. Sin embargo, se utilizan canvas y JavaScript, que tienen dos propósitos:

  • Visualice la curva Bézier básica (curva roja).
  • Permita el ajuste de curvas con la interfaz de usuario de "ruta" típica.

Puede arrastrar los dos puntos finales (puntos negros) y los dos puntos de control (cuadrados negros). El código JavaScript actualizará la animación en consecuencia mediante la actualización de algunas variables CSS.

Nota: Aquí un versión CSS pura para referencia.

cómo funciona

Supongamos que la curva Bezier cúbica deseada está definida por cuatro puntos: p0, p1, p2y p3Establecemos las reglas de CSS de la siguiente manera:

/* pseudo CSS code */
div {
  animation-name: move-x, move-y;
  /*
    Define:
    f(x, a, b) = (x - a) / (b - a)
    qx1 = f(p1.x, p0.x, p3.x)
    qx2 = f(p2.x, p0.x, p3.x)
    qy1 = f(p1.y, p0.y, p3.y)
    qy2 = f(p2.y, p0.y, p3.y)
  */
  animation-timing-function: 
    cubic-bezier(1/3, qx1, 2/3, qx1),
    cubic-bezier(1/3, qy1, 2/3, qy2);
}

@keyframes move-x {
  from {
    left: p0.x;
  }
  to {
    left: p3.x;
  }
}

@keyframes move-y {
  from {
    top: p0.y;
  }
  to {
    top: p3.y;
  }
}

los @keyframes normas move-x y move-y definir la ubicación inicial y final del elemento animation-timing-function tenemos dos magia cubic-bezier() funciones, los parámetros se calculan de modo que ambos top y left tener siempre los valores correctos en todo momento.

Me saltaré las matemáticas, pero hice una prueba rápida. aquípara sus mentes matemáticamente curiosas.

discusiones

Este método debería funcionar bien en la mayoría de los casos. Incluso puede hacer una curva Bézier cúbica en 3D ingresando otra animación para z valor.

Sin embargo, hay algunas advertencias menores:

  • No funciona cuando ambos extremos se encuentran en una línea horizontal o vertical debido al error de división por cero.

Nota: en la práctica, puede simplemente agregar un pequeño desplazamiento como solución alternativa.

  • No admite curvas Bezier de orden superior a 3.
  • Las opciones de sincronización de animación son limitadas.
    • Usamos 1/3 y 2/3 anterior para lograr la sincronización de línea.
    • Puede cambiar ambos valores para ajustar el tiempo, pero esto es limitado en comparación con otros métodos. Más sobre eso más adelante.

Método 2: animaciones competitivas

Como calentamiento, imagina un elemento con dos animaciones:

div {
  animation-name: move1, move2;
}

¿Cuál es la ruta de movimiento del elemento si las animaciones se definen de la siguiente manera:

@keyframes move1 {
  to {
    left: 256px;
  }
}

@keyframes move2 {
  to {
    top: 256px;
  }
}

Como puede suponer, se mueve en diagonal:

¿Qué pasaría si las animaciones se definieran así:

@keyframes move1 {
  to {
    transform: translateX(256px);
  }
}

@keyframes move2 {
  to {
    transform: translateY(256px);
  }
}

“¡Ajá, no me puedes engañar!”, podrías decir, ya que habrás notado que ambas animaciones cambian la misma propiedad, “move2 debe cancelar move1 como esto:"

Bueno, yo también solía pensar eso, pero en realidad obtenemos esto:

ese es el truco move2 no hay un from fotograma, lo que significa que la posición de inicio está animada por move1.

En la siguiente demostración, la posición inicial del move2 se visualiza como un punto azul en movimiento:

Curvas de Bézier cuadráticas

La manifestación de la derecha anterior se asemeja a la construcción de una curva Bezier cuadrática:

Bézier 2 grandes
Phil Tregoning, Dominio público, a través de Wikimedia Commons

Pero se ven diferentes. La construcción tiene tres puntos que se mueven linealmente (dos verdes, uno negro), pero nuestra demostración solo tiene dos (el punto azul y el elemento de destino).

En realidad, la ruta de movimiento en la demostración. es curva de Bezier cuadrática, solo necesitamos ajustar cuidadosamente los fotogramas clave. lo extrañaré matemáticas y solo revela la magia:

Supongamos que una curva de Bézier cuadrática está definida por puntos p0, p1y p2Para mover un elemento a lo largo de la curva, hacemos lo siguiente:

/* pseudo-CSS code */
div {
  animation-name: move1, move2;
}

@keyframes move1 {
  from {
    transform: translate3d(p0.x, p0.y, p0.z);
  }
  /* define q1 = (2 * p1 - p2) */
  to {
    transform: translate3d(q1.x, q1.y, q1.z);
  }
}

@keyframes move2 {
  to {
    transform: translate3d(p2.x, p2.y, p2.z);
  }
}

como la demostración de Método 1puede ver o ajustar la curva. Además, la demostración muestra dos datos más:

  • La construcción matemática (partes móviles grises)
  • Animaciones CSS (partes azules)

Ambos se pueden alternar con las casillas de verificación.

Curvas cúbicas de Bézier

Este método también funciona para curvas Bézier cúbicas si la curva está definida por puntos p0, p1, p2y p3.Las animaciones deben definirse así:

/* pseudo-CSS code */
div {
  animation-name: move1, move2, move3;
}

@keyframes move1 {
  from {
    transform: translate3d(p0.x, p0.y, p0.z);
  }
  /* define q1 = (3 * p1 - 3 * p2 + p3) */
  to {
    transform: translate3d(q1.x, q1.y, q1.z);
  }
}

@keyframes move2 {
  /* define q2 = (3 * p2 - 2 * p3) */
  to {
    transform: translate3d(q2.x, q2.y, q2.z);
  }
}

@keyframes move3 {
  to {
    transform: translate3d(p3.x, p3.y, p3.z);
  }
}

Extensiones

¿Qué hay de las curvas 3D Bezier? De hecho, lo cierto es que todos los ejemplos anteriores fueron Curvas 3D, simplemente nunca nos ocupamos de ellas z valores.

¿Qué pasa con las curvas de Bezier de orden superior? Estoy 90% seguro de que el método puede extenderse naturalmente a órdenes superiores. Avíseme si ha calculado la fórmula para las curvas de Bézier de cuarto orden o, mejor aún, una fórmula general para las curvas de Bézier de N orden.

Método 3: construcción de la curva Bezier estándar

los construcción matemática de las curvas de Bezier ya nos da una buena pista.

Bézier 3 grandes
Phil Tregoning, Dominio público, a través de Wikimedia Commons

Paso a paso podemos determinar las coordenadas de todos los puntos en movimiento. Primero determinamos la ubicación del punto verde que se mueve entre p0 y p1:

@keyframes green0 {
  from {
    --green0x: var(--p0x);
    --green0y: var(--p0y);
  }
  to {
    --green0x: var(--p1x);
    --green0y: var(--p1y);
  }
}

Se pueden construir puntos verdes adicionales de manera similar.

Entonces podemos determinar la ubicación de un punto azul de la siguiente manera:

@keyframes blue0 {
  from {
    --blue0x: var(--green0x);
    --blue0y: var(--green0y);
  }
  to {
    --blue0x: var(--green1x);
    --blue0y: var(--green1y);
  }
}

Enjuague y repita, eventualmente obtendremos la curva deseada.

Similar a Método 2con este método podemos construir fácilmente una curva Bezier 3D. También es intuitivo extender el método a curvas de Bézier de orden superior.

El único inconveniente es el uso de @propertyque no es compatible con todos los navegadores.

Tiempo de animación

Todos los ejemplos hasta ahora tienen sincronización "lineal", ¿qué pasa con la aceleración u otras características de sincronización?

Nota: Por "lineal" queremos decir La variable t en la curva cambia linealmente de 0 a 1. En otras palabras, t es lo mismo que el progreso de la animación.

animation-timing-function nunca usado en Método 2 y Método 3Al igual que otras animaciones CSS, aquí podemos usar cualquier función de sincronización admitida, pero debemos implementar la misma función para todas las animaciones (move1, move2y move3) al mismo tiempo.

Aquí hay un ejemplo de animation-timing-function: cubic-bezier(1, 0.1, 0, 0.9):

Y así es como se ve con animation-timing-function: steps(18, end):

Por otra parte, Método 1 es más difícil porque ya utiliza un cubic-bezier(u1, v1, u2, v2) función de tiempo En los ejemplos anteriores tenemos u1=1/3 y u2=2/3De hecho, podemos ajustar el tiempo cambiando ambos parámetros. De nuevo, todas las animaciones (p. ej. move-x y move-y) debe tener los mismos valores de u1 y u2.

Esto es lo que parece cuando u1=1 y u2=0:

con Método 2podemos lograr exactamente el mismo efecto afinando animation-timing-function a cubic-bezier(1, 0.333, 0, 0.667):

En realidad, funciona de una manera más general:

Supongamos que se nos da una curva Bézier cúbica y hemos creado dos animaciones para la curva con Método 1 y Método 2 Para cualquier valor válido de u1 y u2las siguientes dos configuraciones tienen el mismo tiempo de animación:

  • Método 1 con animation-timing-function: cubic-bezier(u1, *, u2, *).
  • Método 2 con animation-timing-function: cubic-bezier(u1, 1/3, u2, 2/3).

Ahora entendemos por qué Método 1 es "limitado": s Método 1 nosotros solo podemos cubic-bezier() con dos parámetros, pero con Método 2 y Método 3 podemos usar cualquier CSS animation-timing-function.

Conclusiones

En este artículo, discutimos 3 métodos diferentes para mover elementos exactamente a lo largo de una curva Bezier usando solo animaciones CSS.

Aunque los tres métodos son más o menos prácticos, tienen sus pros y sus contras:

  • Método 1 puede ser más intuitivo para aquellos familiarizados con la piratería de sincronización, pero es menos flexible con la sincronización de animación.
  • Método 2 hay reglas CSS muy simples. Cualquier función de tiempo CSS se puede aplicar directamente. Sin embargo, puede ser difícil recordar las fórmulas.
  • Método 3 tiene más sentido para aquellos familiarizados con la construcción matemática de las curvas de Bezier. El momento de la La animación también es flexible. Por otro lado, no todos los navegadores modernos son compatibles debido al uso de @property.

¡Eso es todo! Espero que encuentres este artículo interesante. ¡Por favor, hágame saber sus pensamientos!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir