Cómo hice un generador para cargadores SVG con opciones Sass y SMIL | Programar Plus

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 le permite pegar una ruta 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.

Una ruleta segmentada redonda en la que cada segmento aparece y desaparece en sucesión para crear un efecto circular.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 en el que aterricé:

@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) de 1 a 12 que aumenta el retraso de la animación con cada :nth-child elemento. 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 retrasos 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 una idea para una galería de cargadores, donde cada cargador está hecho del mismo bucle Sass. Siempre me cuesta encontrar este tipo de cosas en línea, así que pensé que podría ser útil para otros, sin mencionarme a mí mismo.

Ya había construido este tipo de cosas antes como un proyecto personal, así que terminé construyendo un generador de carga. ¡Avísame si encuentras errores en él!

Un cargador, dos salidas

Fui bloqueado por mis propias habilidades de desarrollador mientras creaba un generador que produce la salida Sass correcta. Decidí probar otro enfoque de animación con animaciones SMIL, y eso es lo que terminé decidiendo usar.

Pero luego recibí algo de ayuda (¡gracias, ekrof!) y después de todo puse a Sass a trabajar.

Entonces, 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 frente a CSS/Sass

Aprendí bastante sobre animaciones SMIL y CSS/Sass en el camino. Estos son algunos de los puntos clave que me ayudaron en mi camino 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 marcado SVG. Eso es algo que ni CSS ni Sass pueden hacer.
  • Las animaciones SMIL se conservan cuando se incrusta un SVG como imagen o como imagen de fondo. Es posible agregar un CSS <style> bloquear directamente dentro del SVG, pero no tanto con Sass, por supuesto. Es por eso que hay una opción para descargar el archivo SVG real al seleccionar la opción SMIL en el generador.
  • 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 aquí, ¡compártala!). Pensé que estaba relacionado con la aceleración de GPU, pero parece que ambos usan el mismo motor de animación.

Dos hilanderos, uno izquierdo y otro derecho.  Ambos son rojos y consisten en círculos que aparecen y desaparecen sucesivamente como un GIF animado.SMIL (izquierda) y Sass (derecha)

Es posible que notes una diferencia en el encadenamiento de las animaciones entre ambos idiomas:

  • solía 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, el W3C señala que [when] varias animaciones intentan modificar la misma propiedad, luego 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 la salida de Sass.

Trabajar con transformaciones

Trabajar con transformaciones en el estilo del cargador fue un gran problema. yo había aplicado transform: rotate en línea con cada forma porque es una forma sencilla de colocarlos uno al lado del otro en un círculo y con una cara apuntando hacia el centro.

<svg>
  <!-- etc. -->
  <use class="loader" xlink:href="https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/#loader" transform="rotate(0 50 50)" />
  <use class="loader" xlink:href="https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/#loader" transform="rotate(30 50 50)" />
  <use class="loader" xlink:href="https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/#loader" transform="rotate(60 50 50)" />
  <!-- etc. -->
</svg>

Podría declarar un type en SMIL con <animateTransform> (p.ej scale o translate) 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 lugar, transform en CSS anulaba cualquier transformación anterior aplicada al SVG en línea. 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é.

Las mismas dos ruletas rojas que antes pero con resultados diferentes.  La versión de SMIL de la izquierda parece funcionar como se esperaba, pero la de Sass de la derecha no se anima en un círculo como debería.

La solución (no muy bonita) para hacer el Sass similar a SMIL fue colocar cada forma dentro de un grupo (<g>) y aplique la rotación en línea a los grupos y la animación a las formas. De esta forma, la transformación en línea no se ve afectada por la animación.

<svg>
  <!-- etc. -->
  <g class="loader" transform="rotate(0 50 50)">
    <use xlink:href="https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/#loader" />
  </g>
  <g class="loader" transform="rotate(30 50 50)">
    <use xlink:href="https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/#loader" />
  </g>
  <!-- etc. -->
</svg>

Ahora ambos lenguajes tienen un resultado muy similar.

La tecnología que usé

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” donde todo el código vive 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 va:

  • Tiene un modelo rápido que lo ayuda a concentrarse en el desarrollo en lugar de la configuración.
  • Hay enrutamiento automático 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 metaetiquetas.

Veamos algunos cargadores de ejemplo.

Lo que me gusta del resultado final es que el generador no es un pony de un solo truco. No hay una sola manera de usarlo. Debido a que genera tanto SMIL como CSS/Sass, hay varias formas de integrar un cargador en su 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 descargue el SVG del generador, cárguelo en su servidor y haga 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 el marcado 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 marcado y dejar que SMIL haga lo suyo.

Use un bucle Sass en el SVG en línea

Esto es lo que originalmente me inspiré a hacer, pero me encontré con algunos obstáculos. En lugar de escribir declaraciones CSS para cada animación, podemos usar el bucle Sass producido por el generador. El bucle se dirige a un .loader clase que ya se aplicó al SVG de salida. 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 de forma personalizada donde puedes agregar texto, emojis o cualquier elemento SVG a la mezcla:

El mismo círculo giratorio pero con formas SVG personalizadas: una palabra, un emoji de caca y un asterisco rosa y naranja brillante.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 con su propio elemento SVG. De esa manera, hay menos con qué trabajar, al tiempo que permite resultados más simples.

El desafío de este proyecto es trabajar con 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 quiero regresar y limpiar ese código desordenado. Dicho esto, el proyecto es de código abierto y las solicitudes de incorporación de cambios son bienvenidas. Siéntase libre 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 bucle de Sass que leí en un libro. No es el código más limpio del mundo, pero estoy impresionado 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 tiene curiosidad acerca de la seguridad de SMIL, es por una buena razón. Hubo un tiempo en que Chrome iba a dejar completamente obsoleto a SMIL (ver la nota de apertura en MDN). Pero esa desaprobación ha sido suspendida y (aparentemente) no se ha hablado en mucho tiempo.

¿Puedo usar SMIL?

Estos datos de soporte del navegador son de Caniuse, que tiene más detalles. Un número indica que el navegador admite la función en esa versión y en adelante.

Escritorio

Cromo Firefox ES DECIR Borde Safari
5 4 No 79 6

Móvil / Tableta

Android cromo android firefox Androide iOSSafari
96 95 3 6.0-6.1