RSS 11 November 2021

Cómo hice un generador para loaders SVG con opciones Sass y SMIL

Generador loaders personalizable y gratuito

#svg#tutorial#espanol

Mientras aprendía Vue.js, comencé a crear herramientas web gratuitas que involucraban la exploración de SVG, ¡con el objetivo de aprender algo sobre ambos! Echemos un vistazo a una de esas herramientas: un generador que crea cargadores SVG y te permite elegir entre animación SMIL o Sass, diferentes estilos, colores, formas y efectos. Incluso permite pegar un path, SVG o texto personalizado y luego descargar el SVG final, copiar el código o abrir una demostración en CodePen.

Cómo empezó

Tres coincidencias me llevaron a construir un generador para cargadores SVG.

Coincidencia 1: el libro de Sarah Drasner

La primera vez que leí sobre los bucles de Sass fue en *SVG Animations* de Sarah Drasner. Ella muestra cómo escalonar animaciones con una función Sass (como lo hace en el Capítulo 6, "Animación de visualizaciones de datos").

Me inspiré en ese capítulo y en las posibilidades de los loops de Sass.

Coincidencia 2: Un GIF

En ese mismo momento de la vida, me pidieron que replicara un elemento "cargador", similar al viejo clásico de Apple.

Esta es una maqueta del cargador que me pidieron que hiciera.

Hice referencia al ejemplo de Sarah para que esto sucediera. Este es el código de bucle Sass que logré:

    @for $i from 1 to 12 {
      .loader:nth-of-type(#{$i}) {
        animation: 1s $i * 0.08s opacityLoader infinite;
      }
    }

    @keyframes opacityLoader {
     to { opacity: 0; }
    }

Esto define una variable para un número (i) del 1 al 12 que aumenta el delay de la animación con cada elemento :nth-child. Fue el caso de uso perfecto para animar tantos elementos como quisiera con solo dos líneas de Sass, ahorrándome declaraciones CSS para cada uno de los delays que necesitaba. Esta es la misma animación, pero escrita en Vanilla CSS para mostrar la diferencia:

    .loader:nth-of-type(1) {
      animation: 1s 0.08s opacityLoader infinite;
    }
    .loader:nth-of-type(2) {
      animation: 1s 0.16s opacityLoader infinite;
    }

    ...

    .loader:nth-of-type(12) {
      animation: 1s 0.96s opacityLoader infinite;
    }
    @keyframes opacityLoader {
      to { opacity: 0; }
    }

Coincidencia 3: Una idea

Con estas cosas en mi cabeza, tuve la idea de crear una galería de loaders, donde cada loader esté hecho con el mismo bucle Sass. Siempre me cuesta encontrar este tipo de assets online, así que pensé que podría ser útil para otros también.

Ya había construido un generador de íconos antes como proyecto personal, así que [terminé construyendo un generador de loaders] (http://holasvg.com/loaders). ¡Avisame si encontras errores!

Un cargador, dos salidas

Estaba bloqueada por mis propias habilidades de desarrolladora mientras creaba un generador que produzca el código Sass correcto. Así que decidí probar otro enfoque de animación con SMIL animations, y eso fue lo que decidí usar.

Pero recibí un poco de ayuda (¡gracias, ekrof!) y terminé agregando ambas opciones al generador. Descubrí que era un desafío hacer que ambos idiomas arrojaran el mismo resultado. De hecho, a veces producen resultados diferentes.

SMIL vs CSS/Sass

Aprendí bastante sobre animaciones SMIL y CSS/Sass en el camino. Estos son algunos de los puntos clave que me ayudaron para hacer el generador:

  • SMIL no depende de ningún recurso externo. Anima SVG a través de atributos de presentación directamente en el SVG. Eso es algo que ni CSS ni Sass pueden hacer.

  • Las animaciones SMIL se conservan cuando se usa un SVG como imagen o como imagen de fondo. Es posible agregar un bloque CSS

  • Las animaciones SMIL se ven un poco más fluidas. No pude encontrar la razón de esto (si alguien tiene más información acá, ¡bienvenida!). Pensé que estaba relacionado con la aceleración de GPU, pero parece que ambos usan el mismo motor de animación.

SMIL (izquierda) y Sass (derecha)

Se puede notar una diferencia en el encadenamiento de las animaciones entre ambos idiomas:

  • Usé additive="sum" en SMIL para agregar animaciones una tras otra. Esto asegura que cada nuevo efecto de animación evite anular la animación anterior.

  • Dicho esto, en CSS/Sass, la W3C señala que [cuando] varias animaciones intentan modificar la misma propiedad, gana la animación más cercana al final de la lista de nombres.

Es por eso que el orden en que se aplican las animaciones puede cambiar el resultado en Sass.

Trabajar con transformaciones

Trabajar con transformaciones en los estilos del loader fue un gran problema. Apliqué transform: rotate inline a cada forma porque es una forma sencilla de colocarlas una al lado de la otra en un círculo y con una cara apuntando hacia el centro.

  <svg>
    ...
    <use class="loader" xlink:href="#loader" transform="rotate(0 50 50)" /> 
    <use class="loader" xlink:href="#loader" transform="rotate(30 50 50)" />
    <use class="loader" xlink:href="#loader" transform="rotate(60 50 50)" />
    ...
  </svg>

Podría declarar un tipo en SMIL con (por ejemplo, escalar o trasladar) para agregar esa transformación específica a la transformación original de cada forma:

  <animateTransform attributeName="transform" 
    type="translate"
    additive="sum" 
    dur="1s"
    :begin="`${i * 0.08}s`"
    repeatCount="indefinite"
    from="0 0"
    to="10"
  />

Pero en cambio, la transformación en CSS anulaba cualquier transformación anterior aplicada al SVG inline. En otras palabras, la posición original se restableció a 0 y mostró un resultado muy diferente al producido por SMIL. Eso significaba que las animaciones terminaron luciendo idénticas sin importar qué.

La solución (no muy bonita) para hacer que Sass sea similar a SMIL fue colocar cada forma dentro de un elemento de grupo () y aplicar la rotación inline a los grupos y la animación a las formas. De esta manera, la transformación inline no se ve afectada por la animación.

  <svg>
    ... 
    <g class="loader" transform="rotate(0 50 50)">
     <use xlink:href="#loader" />
    </g>
    <g class="loader" transform="rotate(30 50 50)">
     <use xlink:href="#loader" />
    </g>
    ...
  </svg>

Ahora ambos lenguajes tienen un resultado muy similar.

La tecnología que utilicé

Usé Vue.js y Nuxt.js. Ambos tienen una excelente documentación, pero hay razones más específicas por las que los elijo.

Me gusta Vue por muchas razones:

  • Vue encapsula HTML, CSS y JavaScript como un "[componente de archivo único] (https://github.com/marianabeldi/holasvg-loaders/blob/main/components/Codes.vue)" donde vive todo el código en un solo archivo con el que es más fácil trabajar.

  • La forma en que Vue vincula y actualiza dinámicamente los atributos HTML o SVG es muy intuitiva.

  • HTML y SVG no requieren transformaciones adicionales (como hacer que el código sea compatible con JSX).

En cuanto a Nuxt:

  • Tiene un modelo rápido que lo ayuda a concentrarse en el desarrollo en lugar de la configuración.

  • Hay rytas automáticas y admite componentes de importación automática.

  • Es una buena estructura de proyecto con páginas, componentes y diseños.

  • Es más fácil optimizar para SEO, gracias a las metatags.

Veamos algunos loaders de ejemplo

Lo que me gusta del resultado final es no hay una sola forma de usarlo. Debido a que genera tanto SMIL como CSS/Sass, hay varias formas de integrar un loader en tu propio proyecto.

Descarga el SMIL SVG y utilízalo como imagen de fondo en CSS

Como mencioné anteriormente, las características de SMIL se conservan cuando se usa un SVG como archivo de imagen de fondo. Entonces, simplemente hay que descargar el SVG del generador, subirlo al servidor y hacer referencia a él en CSS como imagen de fondo.

De manera similar, podríamos usar el SVG como imagen de fondo de un pseudo-elemento:

Coloque el SVG directamente en HTML

El SVG no tiene que ser una imagen de fondo. Es solo código, después de todo. Eso significa que simplemente podemos colocar el código del generador en nuestro propio código y dejar que SMIL haga lo suyo.

Usar un loop Sass en el SVG inline

Esto es lo que originalmente me inspiró, pero me encontré con algunos obstáculos. En lugar de escribir declaraciones CSS para cada animación, podemos usar el loop Sass producido por el generador. El loop apunta a una clase .loader que ya se aplicó al SVG generado. Entonces, una vez que Sass se compila en CSS, obtenemos una buena animación giratoria.

Todavía estoy trabajando en esto

Mi parte favorita del generador es la opción personalizada donde puedes agregar texto, emojis o cualquier elemento SVG a la mezcla:

Texto personalizado, emoji y SVG

Lo que me gustaría hacer es agregar una tercera opción para que los estilos tengan solo un elemento en el que pueda trabajar y permitir resultados más simples.

El desafío de este proyecto es agregar opción de valores personalizados para muchas cosas, como duración, dirección, distancia y grados. Otro desafío para mí personalmente es familiarizarme más con Vue porque necesitaría limpiar el código desordenado. Dicho esto, el proyecto es de código abierto, ¡y las solicitudes para hacer cambios son bienvenidas! Sean librs de enviar sugerencias, comentarios o incluso recomendaciones de cursos de Vue, especialmente aquellos relacionados con SVG o la creación de generadores.

Todo esto comenzó con un loop de Sass que leí en un libro. No es el código más limpio del mundo, pero estoy impresionada por el poder de las animaciones SMIL. Recomiendo encarecidamente la guía de Sarah Soueidan para profundizar en lo que SMIL es capaz de hacer.

Si tienen curiosidad acerca de la seguridad de SMIL, es por una buena razón. Hubo un momento en que Chrome iba a deprecar por completo SMIL (ver la nota de apertura en MDN). Pero lo suspendiaron y no se volviío a hablar de esto (aparentemente).

¿Puedo usar SMIL?

Este artículo se publicó por primera vez en CSS-Tricks . Gracias a las ediciones de Chris Coyier y Geoff Graham

Let's stay in touch.

mbeldi@gmail.com