Cómo acercarse a las animaciones SVG en CSS
Publicado: 2022-03-11Las animaciones son una parte omnipresente de la web. A diferencia de las imágenes GIF parpadeantes que plagaron los sitios web en los primeros días de Internet, las animaciones de hoy son más sutiles y de buen gusto. Los diseñadores y especialistas front-end los utilizan para hacer que los sitios web se vean más pulidos, mejorar la experiencia del usuario, llamar la atención sobre elementos importantes y transmitir información.
Los desarrolladores web pueden beneficiarse de la combinación de la potencia de SVG y CSS para crear animaciones sin utilizar bibliotecas externas. Este tutorial de animación SVG muestra cómo crear animaciones personalizadas para proyectos del mundo real.
Animación SVG usando CSS: conceptos básicos
Antes de animar SVG con CSS, los desarrolladores deben comprender cómo funcionan internamente los SVG. Afortunadamente, es similar a HTML: definimos los elementos SVG con sintaxis XML y los diseñamos con CSS, como si fueran HTML.
Los elementos SVG están diseñados específicamente para dibujar gráficos. Podemos usar <rect> para dibujar un rectángulo, <circle> para dibujar círculos, etc. SVG también define <ellipse> , <line> , <polyline> , <polygon> y <path> .
Nota: La lista completa de elementos SVG incluso incluye <animate> , que le permite crear animaciones utilizando el lenguaje de integración multimedia sincronizada (SMIL). Sin embargo, su futuro es incierto y el equipo de Chromium recomienda utilizar un enfoque basado en CSS o JavaScript para animar archivos SVG siempre que sea posible.
Los atributos disponibles dependen del elemento, por lo que mientras <rect> tiene atributos de width y height , el elemento <circle> tiene el atributo r , que define su radio.
Si bien la mayoría de los elementos HTML pueden tener elementos secundarios, la mayoría de los elementos SVG no pueden. Una excepción es el elemento de grupo <g> , que podemos usar para aplicar estilos CSS y transformaciones a varios elementos a la vez.
El elemento <svg> y sus atributos
Otra diferencia importante entre HTML y SVG es cómo colocamos los elementos, especialmente a través del atributo viewBox de un elemento externo <svg> dado. Su valor consta de cuatro números separados por espacios en blanco o una coma: min-x , min-y , width y height . Juntos, estos especifican cuánto de nuestro dibujo SVG queremos que el navegador procese. Esa área se escalará para ajustarse a los límites de la ventana gráfica , según lo definido por los atributos de width y height del elemento <svg> .
Cuando se trata de letterboxing, la proporción de los atributos de width y height de la ventana gráfica puede diferir de la proporción de las partes de width y height del atributo viewBox .
De forma predeterminada, la relación de aspecto del lienzo SVG se conservará a expensas de un viewBox más grande que el especificado, lo que provocará una representación más pequeña y con formato de buzón dentro de la ventana gráfica. Pero puede especificar un comportamiento diferente a través del atributo preserveAspectRatio .
Esto nos permite dibujar imágenes de forma aislada y estar seguros de que todos los elementos se colocarán correctamente sin importar el contexto o el tamaño de representación.
Si bien puede codificar imágenes SVG a mano, las imágenes más complejas pueden requerir un programa de gráficos vectoriales (nuestro tutorial de animación SVG demuestra ambas técnicas). Mi editor de elección es Affinity Designer, pero cualquier editor debe proporcionar suficiente funcionalidad para las operaciones simples que se tratan aquí.
Transiciones y animaciones CSS
Las transiciones de CSS nos permiten definir la velocidad y la duración de los cambios de propiedad. En lugar de saltar instantáneamente del valor inicial al valor final, los valores cambian suavemente como en este ejemplo en el que el color de un círculo SVG cambia cuando pasa el mouse sobre él:
Vea el ejemplo de Pen Transition de Filip Defar (@dabrorius) en CodePen.
Podemos definir transiciones con la propiedad de transition , que acepta el nombre de la propiedad que queremos cambiar, la duración de la transición, una función de tiempo de transición (también conocida como función de aceleración) y la duración del retraso antes de que comience el efecto. :
/* property name | duration | easing function | delay */ transition: margin-right 4s ease-in-out 1s;Podemos definir transiciones para múltiples propiedades CSS, cada una de las cuales puede tener valores de transición separados. Sin embargo, existen dos limitaciones obvias en este enfoque.
La primera limitación es que las transiciones se activan automáticamente cuando cambia el valor de una propiedad. Esto es un inconveniente en algunos casos de uso. Por ejemplo, no podemos tener una animación que se repite infinitamente.
La segunda limitación es que las transiciones siempre tienen dos pasos: el estado inicial y el estado final. Podemos extender la duración de la animación, pero no podemos agregar diferentes fotogramas clave.
Por eso existe un concepto más potente: las animaciones CSS. Con animaciones CSS, podemos tener múltiples fotogramas clave y un bucle infinito:
Vea el ejemplo de Pen Animation de Filip Defar (@dabrorius) en CodePen.
Para animar las propiedades de CSS en varios fotogramas clave, primero debemos definir los fotogramas clave mediante una regla @keyframes . El tiempo de los fotogramas clave se define en unidades relativas (porcentajes) porque, en este punto, aún no hemos definido la duración de la animación. Cada fotograma clave describe los valores de una o más propiedades CSS en ese momento. Las animaciones CSS garantizarán transiciones fluidas entre fotogramas clave.
Aplicamos la animación con fotogramas clave descritos al elemento deseado usando la propiedad de animation . Similar a la propiedad de transition , acepta una duración, una función de aceleración y un retraso.
La única diferencia es que el primer parámetro es nuestro nombre @keyframes en lugar de un nombre de propiedad:
/* @keyframes name | duration | easing-function | delay */ animation: my-sliding-animation 3s linear 1s;Animación de una hamburguesa Alternar menú
Ahora que tenemos una comprensión básica de cómo funciona la animación de SVG, podemos comenzar a crear una animación clásica: un menú que cambia suavemente entre un icono de "hamburguesa" y un botón de cierre (una "X"):
Vea la hamburguesa Pen de Filip Defar (@dabrorius) en CodePen.
Esta es una animación sutil pero valiosa. Llama la atención del usuario, informándole que el icono se puede utilizar para cerrar el menú.
Comenzamos nuestra demostración creando un elemento SVG con tres líneas:
<svg class="hamburger"> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--top" /> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--mid" /> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--bot" /> </svg> Cada línea tiene dos conjuntos de atributos. x1 e y1 representan las coordenadas del inicio de la línea, mientras que x2 e y2 representan las coordenadas del final de la línea. Hemos usado unidades relativas para establecer posiciones. Esta es una forma sencilla de garantizar que el contenido de la imagen cambie de tamaño para que se ajuste al elemento SVG que lo contiene. Si bien este enfoque funciona en este caso, hay un gran inconveniente: no podemos mantener la relación de aspecto de los elementos colocados de esta manera. Para eso, tendríamos que usar el atributo viewBox del elemento <svg> .
Tenga en cuenta que aplicamos clases CSS a elementos SVG. Hay muchas propiedades que se pueden cambiar a través de CSS, así que apliquemos algunos estilos básicos a nuestros elementos SVG.
Estableceremos el tamaño del elemento <svg> y cambiaremos el tipo de cursor para indicar que se puede hacer clic en él. Pero para establecer el color y el grosor de las líneas, usaremos las propiedades de stroke stroke-width . Es posible que haya esperado usar color o border , pero a diferencia de <svg> , los subelementos SVG no son elementos HTML, por lo que a menudo tienen diferentes nombres de propiedades:
.hamburger { width: 62px; height: 62px; cursor: pointer; } .hamburger__bar { stroke: white; stroke-width: 10%; } Si renderizamos en este punto, veremos que las tres líneas tienen el mismo tamaño y posición, superponiéndose entre sí por completo. Desafortunadamente, no podemos cambiar las posiciones inicial y final de forma independiente a través de CSS, pero podemos mover elementos completos. Muevamos las barras superior e inferior con la propiedad transform CSS:
.hamburger__bar--top { transform: translateY(-40%); } .hamburger__bar--bot { transform: translateY(40%); }Al mover las barras en el eje Y, obtenemos una hamburguesa de aspecto decente.
Ahora es el momento de codificar nuestro segundo estado: el botón de cerrar. Confiamos en una clase CSS .is-opened .is aplicada al elemento SVG para alternar entre los dos estados. Para que el resultado sea más accesible, envolvamos nuestro SVG en un elemento <button> y manejemos los clics en ese nivel.
El proceso de agregar y eliminar la clase será manejado por un simple fragmento de JavaScript:
const hamburger = document.querySelector("button"); hamburger.addEventListener("click", () => { hamburger.classList.toggle("is-opened"); }); Para crear nuestra X, podemos aplicar una propiedad de transform diferente a nuestras barras de hamburguesas. Debido a que la nueva propiedad de transform anulará la anterior, nuestro punto de partida será la posición original compartida de las tres barras.
A partir de ahí, podemos girar la barra superior 45 grados en el sentido de las agujas del reloj alrededor de su centro y girar la barra inferior 45 grados en el sentido contrario a las agujas del reloj. Podemos encoger la barra del medio horizontalmente hasta que sea lo suficientemente estrecha como para ocultarse detrás del centro de la X:

.is-opened .hamburger__bar--top { transform: rotate(45deg); } .is-opened .hamburger__bar--mid { transform: scaleX(0.1); } .is-opened .hamburger__bar--bot { transform: rotate(-45deg); } De forma predeterminada, la propiedad transform-origin para los elementos SVG normalmente es 0,0 . Esto significa que nuestras barras girarán alrededor de la esquina superior izquierda de la ventana gráfica, pero queremos que giren alrededor del centro. Para solucionar esto, establezcamos la propiedad transform-origin en el center de la clase .hamburger__bar .
Animación de propiedades CSS con transition
La propiedad de CSS de transition le dice al navegador que realice una transición suave entre dos estados diferentes de propiedades de CSS. Aquí queremos animar nuestros cambios a la propiedad de transform , que dicta las posiciones, la orientación y la escala de las barras.
También podemos controlar la duración de la transición usando la propiedad de transition-duration de la transición. Para que la animación se vea ágil, estableceremos una duración corta de 0,3 segundos:
.hamburger__bar { transition-property: transform; transition-duration: 0.3s; ... }La única pieza de JavaScript que necesitamos es el bit que hace que el estado del icono se pueda alternar:
const hamburger = document.querySelector("button"); hamburger.addEventListener("click", () => { hamburger.classList.toggle("is-opened"); }); Aquí, seleccionamos el elemento SVG externo por su clase .mute usando querySelector() . Luego agregamos un detector de eventos de clic. Cuando se activa un evento de clic, alternamos la clase .is-active solo en el propio <svg> , no más abajo en la jerarquía. Debido a que hicimos que la animación CSS solo se aplique a los elementos con la clase .is-active , alternar esta clase activará y desactivará la animación.
Como toque final, convertiremos el cuerpo HTML en un contenedor flexible, lo que nos ayudará a centrar el ícono horizontal y verticalmente. También actualizaremos el color de fondo a un gris muy oscuro y el color del ícono a blanco, para lograr una apariencia elegante de "modo oscuro":
body { display: flex; justify-content: center; align-items: center; background-color: #222; height: 100vh; }Con eso, hemos creado un botón animado completamente funcional usando CSS básico y un fragmento corto de JavaScript. Es fácil cambiar las transformaciones que hemos aplicado para hacer una variedad de animaciones. Los lectores pueden simplemente bifurcar CodePen, que incluye un poco de CSS adicional para pulir, y ser creativos.
Trabajar con datos SVG de editores externos
Nuestra carta de hamburguesas es sumamente sencilla. ¿Y si queremos hacer algo más complejo? Ahí es donde codificar SVG a mano se vuelve difícil y el software de edición de gráficos vectoriales puede ayudar.
Nuestra segunda animación SVG es un botón de silencio que muestra un icono de auriculares. Cuando la música esté activa, el icono pulsará y bailará; cuando está silenciado, el icono aparecerá tachado:
Vea el botón Pen Mute - 5 - tachado en rojo por Filip Defar (@dabrorius) en CodePen.
Dibujar íconos estaría fuera del alcance de este tutorial (y probablemente de la descripción de su trabajo), por lo que comenzaremos con un ícono SVG predibujado. También querremos el mismo estilo de body que nuestro ejemplo de menú de hamburguesas.
Es posible que desee limpiar el código SVG antes de trabajar con él. Puede hacerlo con svgo, una herramienta de optimización de SVG de código abierto basada en Node.js. Esto eliminará elementos innecesarios y hará que el código sea más fácil de editar a mano, lo que deberá hacer para agregar clases y combinar diferentes elementos.
Es poco probable que los iconos SVG creados en el software de edición de imágenes usen unidades relativas. Además, queremos asegurarnos de que se mantenga la relación de aspecto del icono, independientemente de la relación de aspecto del elemento SVG que lo contenga. Para hacer posible este nivel de control, usaremos el atributo viewBox .
Es una buena idea cambiar el tamaño del SVG para que viewBox se pueda configurar en algunos valores fáciles de usar. En este caso, lo convertí a un viewBox de 100 x 100 píxeles.
Asegurémonos de que el icono esté centrado y tenga el tamaño adecuado. Aplicaremos la clase de mute a nuestro elemento SVG base y luego agregaremos los siguientes estilos CSS:
.mute { fill: white; width: 80px; height: 70px; cursor: pointer; } Aquí, el width es ligeramente mayor que la height para evitar el recorte durante las rotaciones de nuestra animación.
Nuestro punto de partida de animación SVG
El SVG ahora limpio contiene un solo elemento <g> que contiene tres elementos <path> .
El elemento de ruta nos permite dibujar líneas, curvas y arcos. Las rutas se describen con una serie de comandos que describen cómo se debe dibujar la forma. Como nuestro ícono consta de tres formas inconexas, tenemos tres caminos para describirlas.
El elemento g SVG es un contenedor que se utiliza para agrupar otros elementos SVG. Lo usamos para aplicar las transformaciones pulsantes y danzantes en los tres caminos simultáneamente.
<svg class="mute" viewBox="0 0 100 100"> <g> <path d="M92.6,50.075C92.213,26.775 73.25,7.938 50,7.938C26.75,7.938 7.775,26.775 7.388,50.075C3.112,51.363 -0.013,55.425 -0.013,60.25L-0.013,72.7C-0.013,78.55 4.575,83.3 10.238,83.3L18.363,83.3L18.363,51.6C18.4,51.338 18.438,51.075 18.438,50.813C18.438,33.275 32.6,19 50,19C67.4,19 81.563,33.275 81.563,50.813C81.563,51.088 81.6,51.338 81.638,51.6L81.638,83.313L89.763,83.313C95.413,83.313 100.013,78.563 100.013,72.713L100.013,60.263C100,55.438 96.875,51.362 92.6,50.075Z" /> <path d="M70.538,54.088L70.538,79.588C70.538,81.625 72.188,83.275 74.225,83.275L74.225,83.325L78.662,83.325L78.662,50.4L74.225,50.4C72.213,50.4 70.538,52.063 70.538,54.088Z" /> <path d="M25.75,50.4L21.313,50.4L21.313,83.325L25.75,83.325L25.75,83.275C27.788,83.275 29.438,81.625 29.438,79.588L29.438,54.088C29.45,52.063 27.775,50.4 25.75,50.4Z" /> </g> </svg> Para hacer que los auriculares latan y bailen, la transition no será suficiente. Este es un ejemplo lo suficientemente complejo como para necesitar fotogramas clave.
En este caso, nuestros fotogramas clave inicial y final (al 0 % y al 100 % de la animación, respectivamente) utilizan un icono de auriculares ligeramente reducido. Para el primer 40% de la animación, aumentamos ligeramente la imagen y la inclinamos 5 grados. Luego, para el próximo 40% de la animación, la escalamos de nuevo a 0.9x y la rotamos 5 grados hacia el otro lado. Finalmente, durante el último 20 % de la animación, la transformación del icono vuelve a los mismos parámetros iniciales para que se reproduzca sin problemas.
@keyframes pulse { 0% { transform: scale(0.9); } 40% { transform: scale(1) rotate(5deg); } 80% { transform: scale(1) rotate(-5deg); } 100% { transform: scale(0.9) rotate(0); } }Optimizaciones de animación CSS
Para mostrar cómo funcionan los fotogramas clave, dejamos nuestro CSS de fotogramas clave más detallado de lo necesario. Hay tres maneras en que podríamos acortarlo.
Dado que nuestro fotograma clave al 100% establece toda la lista de transform , si tuviéramos que omitir la rotate() por completo, su valor predeterminado sería 0:
100% { transform: scale(0.9); } En segundo lugar, sabemos que queremos que coincidan nuestros fotogramas clave de 0% y 100% porque estamos repitiendo la animación. Al definirlos con la misma regla CSS, no tendremos que recordar modificar ambos si queremos cambiar este punto compartido en el ciclo de animación:
0%, 100% { transform: scale(0.9); } Por último, pronto aplicaremos transform: scale(0.9); a la clase mute__headphones , y cuando lo hagamos, ¡no necesitaremos definir los fotogramas clave iniciales y finales en absoluto! Por defecto usarán el estilo estático usado por mute__headphones .
Ahora que hemos definido los fotogramas clave de la animación, podemos aplicar la animación. Agregamos la clase .mute__headphones al elemento <g> para que afecte las tres partes del icono de los auriculares. Primero, una vez más configuramos transform-origin en el center , ya que queremos que el ícono gire alrededor de su centro. También lo escalamos para que su tamaño coincida con el fotograma clave de la animación inicial. Sin este paso, cambiar del icono estático "silenciado" al animado siempre resultará en un aumento repentino de tamaño. (De cualquier manera, volver a silenciar provocará un salto en la escala, y probablemente también una rotación, si el usuario hace clic mientras la escala es mayor a 0.9x. No podemos hacer mucho sobre ese efecto solo con CSS).
Aplicamos la animación usando la propiedad CSS de animation , pero solo cuando la clase principal .is-active está presente, de forma similar a como animamos nuestro menú de hamburguesas.
.mute__headphones { transform-origin: center; transform: scale(0.9); } .is-active .mute__headphones { animation: pulse 2s infinite; }El JavaScript que necesitamos para permitirnos cambiar entre estados también sigue el mismo patrón que el menú de hamburguesas:
const muteButton = document.querySelector(".mute"); muteButton.addEventListener("click", () => { muteButton.classList.toggle("is-active"); }); La siguiente pieza que agregaremos es una línea tachada que aparece cuando el ícono está inactivo. Como este es un elemento de diseño simple, podemos codificarlo manualmente. Aquí es donde es útil tener valores viewBox simples y razonables. Sabemos que los bordes del lienzo están en 0 y 100, por lo que es fácil calcular las posiciones inicial y final de nuestra línea:
<line x1="12" y1="12" x2="88" y2="88" class="mute__strikethrough" />Cambio de tamaño frente a uso de unidades relativas
Se puede hacer un caso para usar unidades relativas en lugar de cambiar el tamaño de la imagen. Esto se aplica en nuestro ejemplo porque estamos agregando solo una línea SVG simple sobre nuestro ícono.
En escenarios del mundo real, es posible que desee combinar contenido SVG más complejo de varias fuentes diferentes. Aquí es donde es útil hacer que todos tengan un tamaño uniforme, ya que no podemos codificar manualmente los valores relativos como hicimos en nuestro ejemplo.
Debido a que aplicamos una clase directamente a nuestro elemento <line> tachado, podemos diseñarlo a través de CSS. Solo debemos asegurarnos de que la línea no esté visible cuando el ícono esté activo:
.mute__strikethrough { stroke: red; opacity: 0.8; stroke-width: 12px; } .is-active .mute__strikethrough { opacity: 0; } Opcionalmente, podemos agregar la clase .is-active directamente al SVG. Esto hará que la animación comience tan pronto como se cargue la página, por lo que cambiamos efectivamente el estado inicial del ícono de no animado (silenciado) a animado (no silenciado).
La animación SVG basada en CSS llegó para quedarse
Solo hemos arañado la superficie de las técnicas de animación CSS y cómo funcionan las ventanas gráficas. Vale la pena saber cómo escribir código SVG a mano para mantener simples las animaciones simples, pero también es importante saber cómo y cuándo utilizar gráficos creados con editores externos. Si bien los navegadores modernos nos permiten crear animaciones impresionantes utilizando solo la funcionalidad integrada, para casos de uso (muy) complejos, es posible que los desarrolladores deseen explorar bibliotecas de animación como GSAP o anime.js.
Las animaciones no tienen que reservarse para proyectos extravagantes. Las modernas técnicas de animación CSS nos permiten crear una amplia gama de animaciones atractivas y pulidas de una manera simple y compatible con todos los navegadores.
¡Un agradecimiento especial a Mike Zeballos por la revisión técnica de este artículo!
