Cómo animar un SVG con imagen de borde

Veamos cómo combinar la border-imagepropiedad en CSS con SVG animados que se mueven alrededor de un borde. En el proceso, cubriremos cómo crear a mano SVG animados de nueve cortes y tamaño variable que puedes usar no solo para recrear el efecto, sino también para hacerlo tuyo.

Esto es lo que estamos haciendo:

En realidad, esto es parte de The Skull, un certificado de captura de bandera en el que estoy trabajando y que está diseñado para explorar las partes internas de Arduino y su microcontrolador. Busqué cómo animar un borde como este, pero no pude encontrar ningún ejemplo útil. La mayoría de las cosas que encontré fueron sobre hormigas marchando, pero desafortunadamente, el stroke-dasharraytruco no funciona con calaveras, y mucho menos con formas más complejas.

Entonces, con el espíritu de aprender y compartir, ¡escribe un blog sobre esto aquí contigo!

¿Deberíamos usar backgroundo border-image?

Al principio ni siquiera sabía border-imageque existía. Intenté usar un ::beforepseudoelemento en mi primer intento y animé su background-positionpropiedad. Eso me llevó hasta aquí:

Como puede ver, funcionó, pero completar el borde requeriría al menos ocho elementos (o pseudoelementos) diferentes. No es ideal saturar el HTML de esa manera.

Publiqué una pregunta en el grupo de Facebook de desarrolladores de CSS israelíes y todos me señalaron la border-imagepropiedad. Hace exactamente lo que dice: usar una imagen (o gradiente CSS) para el borde de un elemento.

Para trabajar con border-image, debe proporcionarle una imagen que se utilice en forma de nueve cortes (piense en un tablero de tres en raya sobre la imagen). Cada una de esas nueve regiones representa una parte diferente del borde: la parte superior, derecha, izquierda e inferior, cada una de las cuatro esquinas y luego el medio (que se ignora).

Por ejemplo, si solo quisiéramos calaveras estáticas, podríamos aprovechar los patrones SVG para repetir la calavera nueve veces. Primero, definimos un patrón de 24×24 usando la ruta del cráneo, y luego usamos este patrón como fillpara un rectángulo de 72×72:

svg version="1.1"  defs  pattern patternUnits="userSpaceOnUse"    path d="..." fill="red"/  /pattern /defs rect fill="url(#skull-fill)" //svg

A continuación, definimos un borde y lo configuramos border-imageen el elemento de destino:

.skulls {  border: 24px solid transparent;  border-image: url("https://skullctf.com/images/skull-9.svg") 24 round;}

Y obtenemos un borde hecho de calaveras:

Agregar animaciones SVG

¡Ahora podemos animar esas calaveras! Funciona, en su mayor parte.

La idea es crear una animación diferente para cada región de la imagen del borde. Por ejemplo, en la esquina superior izquierda, tenemos un cráneo que va de derecha a izquierda, mientras que un segundo cráneo va de arriba a abajo al mismo tiempo.

Animaremos la transformpropiedad para el movimiento. También aprovecharemos los SVG usepara evitar repetir la larga pathdefinición de cada cráneo:

svg version="1.1"  xmlns_xlink="http://www.w3.org/1999/xlink" style  @keyframes left {to {transform: translate(-32px, 0)}}  @keyframes down {to {transform: translate(0, 32px)}} /style defs  path d="..." fill="red"/ /defs !-- Top-left corner: one skull goes left, another goes down -- use href="#skull" x="0" y="0" / use href="#skull" x="32" y="0"//svg

La sintaxis de animación SVG puede resultar familiar allí, porque en lugar de alguna sintaxis específica de SVG, como SMIL, solo usa animaciones CSS. ¿Guay, verdad?

Esto es lo que obtenemos:

Y si agregamos una cuadrícula, podemos ver cómo esta animación también cubre algunos de los bordes superior e izquierdo:

Comienza a verse más impresionante después de agregar los tres bordes restantes, cubriendo así completamente las ocho regiones de la imagen del borde:

svg version="1.1"  xmlns_xlink="http://www.w3.org/1999/xlink" style  @keyframes left {to {transform: translate(-32px, 0)}}  @keyframes down {to {transform: translate(0, 32px)}}  @keyframes right {to {transform: translate(32px, 0)}}  @keyframes up {to {transform: translate(0, -32px)}} /style defs  path d="..." fill="red"/ /defs !-- Top-left corner: one skull goes left, another goes down -- use href="#skull" x="0" y="0" / use href="#skull" x="32" y="0"/ !-- Top-right corner: one skull goes up, another goes left -- use href="#skull" x="64" y="0"/ use href="#skull" x="64" y="32"/ !-- Bottom-left corner: one skull goes down, another goes right -- use href="#skull" x="0" y="32"/ use href="#skull" x="0" y="64"/ !-- Bottom-right corner: one skull goes right, another goes up -- use href="#skull" x="32" y="64"/ use href="#skull" x="64" y="64"//svg

Y esto nos da un circuito completo:

Juntando todo, usamos el SVG animado que acabamos de crear como border-imagey obtenemos el resultado deseado:

Podría jugar con esto todo el día...

Una vez que logré que esto funcionara, comencé a modificar las propiedades de la animación. Esta es una de las ventajas de usar SVG en lugar de GIF: cambiar la naturaleza de la animación es tan fácil como cambiar una propiedad CSS en el archivo fuente SVG, y podrá ver el resultado al instante, sin mencionar los tamaños de archivo más pequeños ( especialmente si se trata de degradados), soporte a todo color y escala nítida.

Para empezar, intenté ver cómo me gustaría si cambiara la función de sincronización de la animación a ease:

También podemos hacer que las calaveras se desvanezcan entre rojo y verde:

Incluso podemos hacer que las calaveras cambien de orientación a medida que recorren la tabla de puntuación más alta:

Dirígete a la pestaña JavaScript donde puedes jugar con la fuente SVG y probarlo tú mismo.

El gigante ? en la habitación (tos, Firefox)

Me sentí muy feliz cuando hice que esto funcionara por primera vez. Sin embargo, hay algunas advertencias que debes tener en cuenta. En primer lugar, por alguna razón, Firefox no muestra la animación en los bordes del borde, sólo en las esquinas:

Curiosamente, si cambié el SVG a un GIF con la misma animación, funcionó perfectamente bien. ¡Pero luego los bordes dejan de animarse en Chrome! ‍♂️

En cualquier caso, parece ser un error del navegador, porque si cambiamos la border-image-repeatpropiedad a stretch, Firefox anima los bordes, pero el resultado es un poco peculiar (aunque probablemente se ajuste al tema de la página):

Cambiar el border-image-repeatvalor a spacetambién parece funcionar, pero solo si el ancho del elemento no es un múltiplo completo del tamaño del cráneo, lo que significa que obtenemos algunos espacios en la animación.

También detecté algunos problemas visuales en los casos en que el tamaño del contenedor no es múltiplo del tamaño del parche (32 px en este caso), como pequeñas líneas negras en los cráneos. Sospecho que esto tiene que ver con algún problema de redondeo de punto flotante. También tiende a romperse al hacer zoom.


¡No es perfecto, pero definitivamente está hecho! Si quieres ver la versión final en acción, te invitamos a visitar la página de puntuaciones más altas de The Skull. ¡Con suerte, tendrá algunos de sus nombres muy pronto!

Deja un comentario

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

Subir