Ya hemos hablado en
otros artículos
sobre cómo animar elementos web, y hemos hecho hincapié en que hay diferentes
formas y herramientas disponibles que debemos conocer antes de empezar a
trabajar en una animación. Cada una tiene sus pros y sus contras, por eso
insistimos en que hay que conocerlas bien para poder optimizar mejor nuestro
tiempo. Pues bien, cuando queremos animar un SVG, una de éstas opciones
(poco utilizadas) es hacerlo con SMIL.
AVISO: No está clara la evolución de SMIL. Tiene una amplio soporte pero podría perder peso en el futuro si los navegadores se decantasen por el uso de animaciones con CSS y de Web Animations API
SMIL es el acrónimo de
Synchronized Multimedia Integration Language, y es un conjunto de etiquetas y
atributos especiales que añadidas dentro de nuestros <svg> nos van a permitir
animar, transicionar o interpolar los valores de los atributos de las
etiquetas que lo conforman. La especificación es muy amplia y no vamos a
listar sus propiedades y valores pero te recomendamos leer artículo de
Sara Soueidan para
CSS-TRICKS.
Nosotros vamos a desgranar esta
demo para
aclarar su funcionamiento y ya puestos para explicar cómo aplicar máscaras y
degradados dentro de un <svg>.

Punto de partida
Vamos a crear un <svg> que contenga algún texto para hacer uso de <text> y
<tspan>, y una etiqueta <defs> en la que definiermos nuestros degradados y
máscaras (<linearGradient> y <mask>). En nuestro caso vamos a utilizar dos
elementos textuales, dos degradados y una máscara compuesta por cien
círculos de diferentes tamaños, por eso para escribirlo y manejarlo más
cómodamente vamos a utilizar PUGJS
para poder hacer uso de algo de JavaScript y escribir bucles y funciones
aritméticas.
Crear degradados y máscaras
Vamos a crear dos degradados lineales usando dos colores (inicio y fin) y a
asignarles una ID única para poder identificarlos, llamarlos y aplicarlos
sobre los elementos que queramos, que en nuestro caso será sobre <text> usando
el atributo [fill].
<defs>
<linearGradient id="gradientMamut" gradientTransform="rotate(0)">
<stop offset="0.5" stop-color="#ff0066"></stop>
<stop offset="0.95" stop-color="#17e2c6"></stop>
</linearGradient>
</defs>
<text x="40" y="75" fill="url(#gradientMamut)">
A continuación y dentro de la misma etiqueta de definición creamos la máscara y le asignamos otra ID para poderla aplicar sobre otros elementos como hicimos anteriormente.
<defs>
...
<mask id="ballons"></mask>
</defs>
<text x="40" y="75" fill="url(#gradientMamut)" mask="url(#ballons)">
Vamos a utilizar un bucle while para crear los círculos que van a conformar la
máscara y para dinamizarlos vamos a apoyarnos en un par de funciones con las que
obtendremos valores aleatorios, de esta manera cada círculo tendrá los mismos
atributos pero con valores distintos y cada vez que se inicialice el resultado
será diferente aunque parecido.
var getRandom = (factor, addition = 0) => {
return Math.floor(Math.random() * factor + addition)
}
var getAlpha = (factor) => {
return Number((Math.random() * factor) / 100).toPrecision(1)
}
while i < 100
- var positionX = getRandom(130)
- var positionY = getRandom(10, 90)
- var radius = getRandom(13, 1)
- var duration = getRandom(5, (20 - radius))
- var red = getRandom(255)
- var green = getRandom(255)
- var blue = getRandom(255)
- var alpha = getAlpha(90)
circle(fill="rgba("+red+", "+green+", "+blue+", "+alpha+")" cx=positionX cy=positionY r=radius)
<defs>
...
<mask id="ballons">
<circle fill="rgba(64, 123, 91, 0.6)" cx="124" cy="91" r="6"></circle>
<circle fill="rgba(190, 99, 182, 0.7)" cx="44" cy="98" r="6"></circle>
<circle fill="rgba(61, 250, 71, 0.7)" cx="46" cy="95" r="12"></circle>
<circle fill="rgba(63, 100, 183, 0.05)" cx="53" cy="95" r="11"></circle>
<circle fill="rgba(52, 189, 254, 0.2)" cx="47" cy="99" r="7"></circle>
</mask>
</defs>
<text x="40" y="75" fill="url(#gradientMamut)" mask="url(#ballons)">
Secuenciando la animación
La animación constará de varios pasos:
- Mostrar el elemento en pantalla
- Mostrar cada elemento de texto
- Modificar los puntos de parada de los colores en los degradados
- Animar la posición en el eje vertical de cada círculo
Animación con CSS
Para presentar el elemento en pantalla vamos a crear una sencilla animación CSS que se dispara tras cargarse el documento y que cambiará la opacidad y posición del elemento para que se haga visible suavemente.
@keyframes show {
0% {
opacity: 0;
transform: translate(0, 2rem);
}
100% {
opacity: 1;
transform: none;
}
}
Animación con SMIL
Para animar las etiquetas que conforman nuestro <svg> vamos a utilizar la
etiqueta <animate> a la que le podemos declarar los atributos que controlan la
animación, siendo los más recurrentes:
[attributeName]- Para indicar qué atributo se va a modificar[values]- Los valores -separados por comas- que van a tomar[dur]- La duración de la animación[repeatCount]- El número de veces que se repetirá[begin]- En qué punto temporal se lanza la animación[calcMode]- Para indicar el modo de interpolar la animación
Así que animaremos los elementos de texto
<text x="40" y="75" mask="url(#ballons)" fill="url(#gradientMamut)">
<tspan dx="-5">yo</tspan>
<animate attributeName="opacity" values="0;1" dur="700ms" repeatCount="1" calcMode="paced"></animate>
<animate attributeName="y" values="80;75" dur="750ms" repeatCount="1" calcMode="paced"></animate>
</text>
Los puntos de parada de los colores de cada degradado
<linearGradient id="gradientMamut" gradientTransform="rotate(0)">
<stop offset="0.5" stop-color="#ff0066">
<animate attributeName="offset" values="0.5;0.1;0.5" repeatCount="indefinite" dur="10s" calcMode="paced"></animate>
</stop>
<stop offset="0.95" stop-color="#17e2c6">
<animate attributeName="offset" values="0.95;0.99;0.95" repeatCount="indefinite" dur="10s" calcMode="paced"></animate>
</stop>
</linearGradient>
Y cada círculo dentro de la máscara, su posición final en ‘Y’ con el valor aleatorio que obtuvimos, su duración que también será aleatoria y su momento de inicio que será incremental.
<circle fill="rgba(78, 196, 61, 0.7)" cx="1" cy="90" r="11">
<animate attributeName="cy" attributeType="XML" values="0;92" dur="10s" repeatCount="indefinite" begin="0.42000000000000004s" calcMode="paced"></animate>
</circle>
Finalmente con la combinación de todo lo anterior, podemos realizar una alegre y colorida tarjeta animada con únicamente una etiqueta HTML, muy poquito CSS y nada de JavaScript.
Conclusión
A pesar de haber combinado técnicas complejas y poco habituales al animar una máscara aplicada a un SVG, apenas hemos descubierto la potencia de SMIL, y no lo vamos a descubrir si de vez en cuando no probamos alternativas al modo y entorno en el que solemos movernos y sentirnos cómodos. A menudo resulta difícil sacar el tiempo y el empuje pero solemos encontrarnos con sorpresas agradables al final del camino.
Para saber más
- Especificación de SMIL
- Guía de animaciones SVG SMIL
- <animate> en MDN
