Use y reutilice todo en SVG … ¡Incluso animaciones! | Programar Plus

Si está familiarizado con las animaciones SVG y CSS y comenzó a trabajar con ellas a menudo, aquí hay algunas ideas que quizás desee tener en cuenta antes de comenzar a trabajar. Este artículo tratará sobre cómo crear y optimizar su código con <use> elemento, variables CSS y animaciones CSS.

Demo en vivo

Parte 1: El elemento SVG

Si es un desarrollador al que le gusta mantener su código SECO o un gran fanático de las variables Sass / CSS, es muy probable que le guste esta etiqueta.

Digamos que tiene un elemento que se repite muchas veces en su gráfico. En lugar de tener una parte compleja de su código repetida muchas veces en su SVG, puede definir esta parte una vez y luego clonarla en otro lugar de su documento con el elemento . Esto no solo reducirá una enorme cantidad de código, sino que también hará que su marcado sea más simple y fácil de manipular.

Para comenzar a implementar el <use> elemento, vaya a su SVG y siga estos pasos:

  1. Identifique la parte del código que desea clonar
  2. Agregar una identificación a esa parte
  3. Conéctelo dentro de su <use> etiqueta como esta: <use xlink:href="https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/#id"/>

¡Eso es! Su nuevo clon está listo, ahora puede cambiar sus atributos (p. Ej. x y y posición) para satisfacer sus necesidades.

Vamos a sumergirnos en un ejemplo muy conveniente.

Quiero compartir este caso real en el que necesitaba animar un gran cubo hecho de pequeñas unidades de cubos. (Imagínese el clásico cubo de Rubik).

Comenzaremos dibujando la unidad del cubo en SVG usando formas y transformaciones básicas:

<svg viewBox="-130 -20 300 100">
  <g id="cube">
    <rect width="21" height="24" transform="skewY(30)"/>
    <rect width="21" height="24" transform="skewY(-30) translate(21 24.3)"/>
    <rect width="21" height="21"  transform="scale(1.41,.81) rotate(45) translate(0 -21)"/>
  </g>
</svg>

Tenga en cuenta que las formas se agrupan en un <g> elemento para que podamos agregar el ID a toda la figura.

A continuación, construyamos un cubo más grande clonando esta unidad. Primero, necesitamos envolver el cubo del ejemplo anterior dentro del <defs> etiqueta dentro del SVG. En el <defs> elemento podemos poner lo que queramos reutilizar, que puede ser una sola forma, un grupo, un degradado … casi cualquier elemento SVG. No se mostrarán en ningún lugar a menos que los usemos fuera de esta etiqueta.

Luego podemos vincular la unidad tantas veces como queramos usando su ID y cambiar el x y y posición en cada clon como este:

<use xlink:href="https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/#cube" x="142" y="124"/>
<use xlink:href="https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/#cube" x="100" y="124"/>
<!-- ... -->

Ahora tenemos que posicionar cada cubo recordando que el último elemento aparecerá en el frente, después de eso tendremos nuestro primer gran cubo listo!

xlink:href está en desuso desde SVG2, pero es mejor usarlo por motivos de compatibilidad. En los navegadores modernos, puede usar href, pero lo probé en Safari y en el momento de escribir este artículo no funciona allí. Si utiliza xlink:href asegúrese de incluir este espacio de nombres en su etiqueta SVG: xmlns:xlink="http://www.w3.org/1999/xlink" (no lo necesitará si decide usar href).

Parte 2: uso de variables CSS para aplicar diferentes estilos a su gráfico reutilizado

Elegí un color principal para el cubo, que es un tono más claro y más oscuro para los lados y un color de trazo. Pero, ¿y si queremos hacer un segundo cubo de un color diferente?

Podemos reemplazar los rellenos y trazos con variables CSS para hacer que estos atributos sean más flexibles. De esa manera, podremos reutilizar la misma unidad de cubo con otra paleta (en lugar de definir una segunda unidad con diferentes colores para un segundo cubo).

¿Por qué no agregar una clase al nuevo cubo y cambiar el color de relleno con CSS? Haremos eso, pero primero, intente inspeccionar un <use> elemento. Notarás que se procesa en Shadow DOM. lo que significa que no es vulnerable a scripts y estilos, como elementos en el DOM normal. Cualesquiera que sean los valores que defina en la figura interior <defs> será heredado por todas sus instancias y no podrá reescribirlas con CSS. Pero si reemplaza esos valores con variables, podrá controlarlos en CSS.

En nuestra unidad de cubo, revisaremos cada lado y reemplazaremos los valores de relleno y trazo con nombres de variables semánticas.

Por ejemplo, esto:

<rect fill="#00affa" stroke="#0079ad" />

… se puede reemplazar con esto:

<rect fill="var(--mainColor)" stroke="var(--strokeColor)" />

A partir de aquí, debemos duplicar el SVG para construir un segundo cubo. Sin embargo, no necesitamos duplicar <defs> si mantenemos ambos en el mismo documento. Podemos agregar una clase a cada SVG y controlar la paleta de colores a través de CSS, redefiniendo los valores de la variable.

Creemos una paleta para el cubo azul y otra para el cubo rosa:

.blue-cube {
  --mainColor: #009CDE;
  --strokeColor: #0079ad;
  --lightColor: #00affa;
  --darkColor: #008bc7;
}

.pink-cube {
  --mainColor: #de0063;
  --strokeColor: #ad004e;
  --lightColor: #fa0070;
  --darkColor: #c7005a;
}

De esta forma, podemos agregar tantos cubos como queramos y cambiar todos los colores desde un solo lugar.

Parte 3: Reutilización de animaciones

La idea para esta instancia es romper los cubos al pasar el mouse, algo así como una vista explosionada para que algunas piezas se alejen del centro cuando coloquemos el cursor sobre los cubos.

Comencemos por definir dos movimientos, uno para cada eje: move Y y move X. Al dividir las animaciones en movimientos, podremos reutilizarlas en cada cubo. Las animaciones consistirán en mover el cubo desde su posición inicial a 30px o 50px en una dirección. Podemos usar una traducción de transformación (X o Y ) para lograrlo. Por ejemplo:

@keyframes moveX {
  to { transform: translateX(-35px);  }
}

Pero si queremos poder reutilizar esta animación, es mejor reemplazar el valor numérico con una variable, así:

@keyframes moveX {
  to { transform: translateX(var(--translate, 35px)); }
}

Si la variable no está definida, el valor predeterminado será 35px.

Ahora necesitamos al menos una clase para enlazar con la animación. En este caso, sin embargo, necesitamos dos clases para mover cubos en el eje x: .m-left y .m-right.

.m-left, .m-right { 
  animation: 2s moveX alternate infinite; 
}

Para que el cubo se mueva hacia la izquierda, necesitamos un valor negativo, pero también podemos declarar un número diferente. Podemos definir nuestra variable así dentro de la .m-left clase:

.m-left { --translate: -50px; }

Lo que está sucediendo aquí es que declaramos que, cuando agregamos la clase .m-left a un elemento, esto reproducirá la animación moveX (el definido en el @keyframes) que tardará dos segundos en trasladarse en el eje xy alcanzar una nueva posición que queda a -50px a la izquierda. Luego, la animación alterna direcciones para que se mueva desde la última posición y tome dos segundos más para volver a su estado original. Y así sucesivamente, porque es un bucle infinito.

Podemos declarar otra variable a la .m-right class pero si no lo hacemos, recuerda que tomará los 35px que declaramos al principio.

El valor por defecto animation-play-state value se está ejecutando, pero tal vez no queremos que los cubos se muevan todo el tiempo. Sería muy molesto y molesto usarlo en un sitio con contenido cercano. Entonces, intentemos reproducir la animación solo al pasar el mouse agregando esto:

svg:hover .m-left {
  animation: 2s moveX alternate infinite;
}

Puede probarlo usted mismo y encontrará que la animación salta súper rápido al estado inicial cada vez que colocamos el cursor fuera del cubo. Para evitarlo podemos sumar el valor paused al final de la animación abreviada:

.m-left {
  animation: 2s moveX alternate infinite paused;
}

Ahora la animación está en pausa pero se ejecutará al pasar el mouse agregando esta línea de CSS:

svg:hover * { 
  animation-play-state: running; 
}

Podemos aplicar cada clase a diferentes elementos en el SVG. En el primer cubo azul, estamos moviendo cubos individuales; en el segundo, aplicamos esas clases a grupos de cubos.

Una última cosa…

No fue hasta más tarde que me di cuenta de que podía reutilizar una sola unidad para construirlos todos. Trabajé en el cubo pequeño para que fuera lo suficientemente isométrico como para que pudiera alinearse fácilmente con los otros al lado. En este punto, mi unidad era una <path>, pero decidí reemplazarlo con formas SVG para reducir el código y obtener un marcado más limpio.

Aprendí que es mejor tomarse un tiempo para analizar lo que se puede hacer con SVG antes de dibujar cada forma y tratar con una gran cantidad de código. Puede que lleve más tiempo al principio, pero le ahorrará mucho tiempo y esfuerzo a largo plazo.