Fenómenos Meteorológicos con PUG y SASS
Gracias a los retos de Codepen he aprendido muchas cosas. Es cierto que a veces hay que tener tiempo para poder dedicárselo a los Challenges, pero si dispones del tiempo suficiente y haces algo que de verdad te interesa puedes llegar a aprender cosas muy chulas.
En esta ocasión, gracias al uso de PUG y SASS (dos nuevas categorías que estrenamos en el blog), conseguí crear varias animaciones de diferentes efectos meteorológicos, como nieve, lluvia y viento. Este reto es de diciembre de 2022, hace ahora un par de años, pero he pensado que sería interesante publicarlo por aquí.
Demo Nieve Demo Lluvia Demo Viento Demo Burbujas
PUG
PUG es un motor de plantillas que facilita la creación de código HTML y que tiene características propias de un lenguaje de programación. Podemos usar variables, condicionales, crear bucles, hacer includes y muchas cosas más que puedes ver detalladas en su documentación. Finalmente lo que obtenemos es un código HTML del de siempre.
SASS
SASS es un preprocesador de CSS que nos permite generar, de manera automática, hojas de estilo. Podemos utilizar variables, bucles, funciones, anidamiento de selectores y muchas otras características que también puedes revisar en detalle en su documentación. Tras la compilación obtendremos un código CSS del de toda la vida.
En el Codepen Challenge Weather Effects de diciembre de 2022, proponían crear varios efectos meteorológicos, uno cada semana del mes.
Nieve / Snow
La primera semana había que crear un efecto de nieve.
See the Pen Snow – CSS by Josetxu (@josetxu) on CodePen.
HTML / PUG
El código HTML lo creamos con PUG de la siguiente manera:
.snow
- for (var i=0; i<200; i++ )
.snowflake
span
Creamos un elemento div
con la clase snow
que será nuestro contenedor general.
Dentro de .snow
(fijaros como está anidado dentro) creamos un bucle que pintará 200 elementos div
con la clase snowflake
.
Y dentro de cada elemento .snowflake
(también anidado dentro) creamos un elemento span
que usaremos para aplicar una animación de giro a cada copo de nieve.
Si queremos más copos de nieve solo tenemos que cambiar el 200 del bucle for
por el número de copos que queramos mostrar, teniendo en cuenta que después, en el CSS, también tendremos que modificar ese número para que todo cuadre.
CSS / SASS
Veamos el código CSS que lo crearemos con SASS, o mejor dicho con SCSS…
SCSS no es más que código SASS pero en lugar de usar la sintaxis propia de SASS donde se eliminan las llaves { }
y los punto y coma ;
al final de cada declaración, SCSS usa una sintaxis más parecida al CSS normal, donde si se usan las llaves { }
y se termina cada declaración con punto y coma ;
igual que haríamos en el CSS de toda la vida. Por lo demás, en cuanto a anidar el código, uso de variables, bucles, etc, es todo igual que en SASS.
.snowflake {
$snowflakes: 200;
position: absolute;
top: -5vmin;
@for $i from 1 through $snowflakes {
&:nth-child(#{$i}) {
opacity: random(90) * 0.01;
font-size: (random(5) * 3px);
left: random(1200) * 0.1vw;
animation: fall-#{$i} (random(5) * 10s) (random(25) * -1.5s) ease-in infinite;
span {
animation: spin (random(5) * 3s) linear 0s infinite;
filter: drop-shadow(0 0 (random(5) * 1px) #fff);
}
}
@keyframes fall-#{$i} {
#{percentage( random(50) / 600 )} {
transform: translateX(0);
}
to {
transform: translateX(calc(100vh + 5vmin));
left: random(1200) * 0.1vw;
}
}
}
span {
display: block;
color: #fff;
&::before {
content: "\2744";
}
}
&:nth-child(4n+2) span::before {
content: "\2745";
}
&:nth-child(4n+3) span::before {
content: "\2747";
}
&:nth-child(4n+4) span::before {
content: "\274B";
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Veamos el código CSS línea por línea.
He obviado las dos primeras reglas para body
, que no tiene nada especial, y para .snow
, que lo único que tiene es que le damos un width
de 120vw
y un margin
de -10vw
para que sea más grande que el body
(tenéis las dos reglas completas en el código en Codepen), y vamos directamente a .snowflake
:
Agarraros que vienen curvas, en concreto 46 curvas…
- Se declara la regla
.snowflake
- Creamos la variable de SASS
$snowflakes
y le damos un valor de 200 (igual que hicimos en el código HTML con PUG). - Le damos posición absoluta.
- Le damos un top de
-5vmin
para que esté arriba del todo, por fuera de la ventana. - Creamos un bucle
@for
con la variable$i
que recorrerá desde 1 a través del valor de$snowflakes
, que como hemos dicho es 200. - Usando la pseudo-clase
:nth-child()
seleccionamos al grupo de hermanos que forman los 200 elementos con la clase.snowflake
, y con la variable$i
creamos una regla para cada uno de ellos, el 1, el 2, el 3… así hasta el número 200. A partir de este punto cada declaración será diferente para cada elemento. - Damos una opacidad aleatoria (para cada elemento) usando la función
random()
de SASS. - Damos un tamaño de fuente aleatorio (para cada elemento) usando la misma función
random()
. - Damos una posición izquierda aleatoria, también con
random()
, para que los copos se distribuyan por todo el ancho de la pantalla. - Aplicamos una animación de caída (declarada en la línea 16) personalizada con la variable
#{$i}
para cada copo de nieve, con valores aleatorios deduration
ydelay
. - Creamos la regla para el elemento
span
que irá dentro de cada.snowflake
. - Aplicamos a cada
span
una animación de giro (declarada en la linea 43) con duración aleatoria para cadaspan
. - Aplicamos también un filtro de sombra aleatorio a cada elemento
span
. - Cerramos la regla del
span
. - Cerramos la regla de los
:nth-child()
. - Con
@keyframes
creamos una animación de caída diferente para cada copo de nieve. Seguimos dentro del bucle y nombramos cada animación con la variable#{$i}
, la misma que ya añadimos en la propiedadanimation
de la línea 10. - Damos un porcentaje inicial a la animación con la función
random()
, estos porcentajes aleatorios irán desde 0,0016% hasta 0,0833%. - Al comienzo de la animación trasladamos el copo en el eje X un valor de cero, o sea… nada.
- Cerramos el primer porcentaje de la animación.
- Con
to
decimos que la animación vaya hasta, que viene a ser lo mismo que poner 100% , hasta el final. - Al final de la animación trasladamos el copo en el eje X un valor de
100vh
(view-height, la altura de la pantalla) y le sumamos5vmin
para que se salga de la pantalla por abajo. - Aplicamos la propiedad
left
con unrandom(1200) * 0.1vw
que irá desde0.1vw
(view-width, el ancho de la pantalla) hasta120vw
, para que se salgan de la pantalla por la derecha y no queden huecos. - Cerramos la animación final del
to
. - Cerramos la animación
@keyframes
. - Y cerramos el bucle
@for
. A partir de aquí estaremos fuera del bucle y los estilos serán ya generales, no aleatorios para cada copo como eran dentro del bucle. - Dentro de cada
snowflake
tenemos un elementospan
que es donde vamos a pintar el copo de nieve. - Le aplicamos un
display
:block
para mostrarlo. - Le decimos que sea de color blanco.
- Creamos dentro su pseudo-elemento
::before
. - En la propiedad
content
del pseudo-elemento pintamos el copo con CSS Entities , en este caso\2744
. - Cerramos el
::before
. - Cerramos el
span
. - Con la pseudo-clase
:nth-child()
seleccionamos elementos de un grupo de hermanos que coincidan con el patrón que especifiquemos, en este caso seleccionaremos cada cuatro elementos (4n) empezando por el segundo elemento (+2) del grupo, y todo junto se expresa así: (4n+2). Pero esto es para seleccionar el elemento.snowflake
, por eso después añadimosspan::before
para que sea el pseudo-elemento delspan
dentro del elemento.snowflake
sobre el que actuemos. - Y pintamos otro copo con otra CSS Entity diferente,
\2745
, recuerda, para cada cuatro elementos , empezando por el segundo. - Cerramos la regla.
- Creamos una nueva serie de elementos hermanos, esta vez seleccionamos cada 4 elementos (4n) empezando por el tercero (+3) y queda: (4n+3).
- Añadimos otra CSS Entity diferente para crear nuevos copos,
\2747
. - Cerramos la regla.
- Una última serie con
:nth-child
, seleccionamos cada cuatro elementos (4n) empezando esta vez por el cuarto (+4), y queda así: (4n+4). - Nueva CSS Entity diferente de las otras,
\274B
, para conseguir el cuarto formato de copo de nieve. - Cerramos la regla.
- Cerramos la regla
.snowflake
, la que abrimos al principio del código. - Con
@keyframes
creamos una animación que llamaremosspin
(aplicada a los copos de nieve en la línea 12). - Decimos que vaya desde
0%
, con un giro de 0 grados. - Hasta
100%
, con un giro de 360 grados. - Cerramos la regla de la animación.
Y ya está, un poco denso, pero es lo que tiene explicar cada línea del código. Si ya conoces el funcionamiento de SASS seguramente lo hayas entendido, de lo contrario si nunca has visto SASS, puede que te haya sonado a chino o hasta que te hayas mareado un poco… En ese caso puedes revisar la documentación de SASS para intentar comprenderlo un poco mejor.
Lluvia / Rain
La segunda semana del reto de Codepen había que crear en efecto de lluvia.
See the Pen Rain – CSS by Josetxu (@josetxu) on CodePen.
HTML / PUG
El HTML, igual que con la nieve, lo creamos con PUG:
.rain
.left
.left.center
.right.center
.right
- for (var i=0; i<500; i++ )
.drop
Creamos un elemento div
con la clase rain
.
Anidados dentro de .rain
creamos cuatro elementos más, que usaremos para cambiar ligeramente la dirección de caída de las gotas de lluvia al hacer :hover
sobre ellos;
.left
a la izquierda del todo.left.center
a la izquierda pero medio centrado.right.center
a la derecha pero medio centrado.right
a la derecha del todo.
Al hacer :hover
en cada uno de estos elemento cambiaremos el ángulo de caída de las gotas y crearemos una especie de efecto de viento en la lluvia.
Al mismo nivel que los elemento anteriores (dentro de .rain
) crearemos el bucle for
que pintara las gotas de lluvia, en este caso vamos a pintar 500 gotas, cambiando ese número cambiaremos el número de gotas de lluvia, y al igual que en la nieve si lo cambiamos aquí habrá que cambiarlo también en el código CSS. Este bucle pintará 500 elementos div con la clase drop
.
CSS / SASS
El CSS lo creamos de nuevo con SCSS, la variante de SASS que ya vimos en el ejemplo anterior. Solo muestro aquí el bucle que crea las gotas de agua, el resto del código CSS lo tenéis en Codepen.
.drop {
$drops: 500;
border: 0.25vmin solid transparent;
border-bottom-color: #abc2e9;
position: absolute;
top: -5vmin;
--angle: 91deg;
@for $i from 1 through $drops {
&:nth-child(#{$i}) {
opacity: random(90) * 0.01;
left: random(1200) * 0.1vw;
border-left-width: random(80) * 0.1vmin;
animation: fall-#{$i} (random(15) * 0.15s) (random(25) * -0.5s) ease-in infinite;
}
@keyframes fall-#{$i} {
#{percentage(random(50) / 500)} { transform: rotate(var(--angle)) translateX(0); }
to { transform: rotate(var(--angle)) translateX(calc(100vh + 5vmin)); }
}
}
}
- Se declara la regla
drop
, que definirá las gotas de lluvia. - Creamos la variable de SASS
$drops
y le damos un valor de 500 (igual que hicimos en el código HTML con PUG). - Aplicamos un borde transparente.
- Decimos que el borde inferior sea de un tono tirando a blanco.
- Le damos posición absoluta.
- Le damos un top de
-5vmin
para que esté arriba del todo, por fuera de la ventana. - Declaramos la custom property de CSS
--angle
con un valor de91deg
. - Creamos un bucle
@for
con la variable$i
que recorrerá desde 1 a través del valor de$drops
, que como hemos dicho es 500. - Usando la pseudo-clase
:nth-child()
seleccionamos al grupo de hermanos que forman los 500 elementos con la clase.drop
, y con la variable$i
creamos una regla para cada uno de ellos, el 1, el 2, el 3… así hasta el número 500. A partir de este punto cada declaración será diferente para cada elemento. - Damos una opacidad aleatoria (para cada elemento) usando la función
random()
de SASS. - Damos una posición izquierda aleatoria, también con
random()
, para que las gotas se distribuyan por todo el ancho de la pantalla. - Damos un tamaño de borde izquierdo aleatorio (para cada elemento) usando la función
random()
. - Aplicamos una animación de caída (declarada en la línea 15) personalizada con la variable
#{$i}
para cada gota de agua, con valores aleatorios deduration
ydelay
. - Cerramos la regla de los
:nth-child()
. - Con
@keyframes
creamos una animación de caída diferente para cada gota de agua. Seguimos dentro del bucle y nombramos cada animación con la variable#{$i}
, la misma que ya añadimos en la propiedadanimation
de la línea 13. - Damos un porcentaje inicial a la animación con la función
random()
, estos porcentajes aleatorios irán desde 0,002% hasta 1%. Y aplicamos la propiedadtransform
con la funciónrotate()
y el valor de la variable--angle
, más la funcióntranslateX()
con valor0
. - Con
to
decimos que la animación vaya hasta, que viene a ser lo mismo que poner 100% , hasta el final. Y aplicamos las mismas funciones pero esta vez entranslateX
le damos un valor decalc(100vh + 5vmin)
, que es la altura del viewheight (100vh) más 5vmin, para que las gotas salgan por debajo de la pantalla. - Cerramos la animación
@keyframes
. - Cerramos el bucle
@for
. - Y cerramos la regla
.drop
.
Y listo, lo único a destacar son los elementos div
que creamos para inclinar la lluvia, que al hacer :hover
sobre ellos cambiaran el valor de la variable --angle
para torcer el ángulo de caída de las gotas, según estemos sobre uno o sobre otro elemento. En la siguiente imagen podéis ver los diferentes elementos con sus clases, los diferentes ángulos aplicados y las diferentes direcciones de las gotas de agua:
Viento / Wind
La tercera semana del reto de Codepen había que crear un efecto de viento.
See the Pen Wind – CSS by Josetxu (@josetxu) on CodePen.
HTML / PUG
El HTML, ya sabes, lo creamos con PUG.
.wind
.direction
- for (var i=0; i<200; i++ )
.leaf
span
Prácticamente igual que la nieve y la lluvia.
Creamos un elemento div
con la clase wind
que será el contenedor general.
Anidado dentro de .wind
un elemento direction
que servirá para cambiar la dirección del viento.
Al mismo nivel que .direction
(dentro de .wind
) creamos el bucle con las hojas que moverá el viento, en este caso ponemos 200, y le decimos que tengan la clase leaf
.
Dentro de cada hoja (dentro de cada elemento .leaf
) creamos un span
para aplicar los estilos de cada hoja.
CSS / SASS
No quiero extenderme mucho explicando todo el CSS del viento, ya que es prácticamente igual que la nieve y la lluvia. Solo describiré la parte de dar estilos a las hojas, que es lo único que es diferente. Lo demás, el bucle, las propiedades aleatorias con la función random()
, las animaciones para cada hoja, etc, es todo igual que en los ejemplos anteriores, y lo podéis ver detalladamente en el código de Codepen.
.leaf {
$leafs: 200;
/***aqui va el bucle for***/
/***lo que sigue es el estilo de las hojas***/
span {
display: block;
color: #fff;
float: left;
background: linear-gradient(45deg, #ff6600 0 48%, #333 48% 52%, #ff5722 0 52%);
border-radius: 20% 100%;
position: relative;
&:before {
content: "";
position: absolute;
width: 10%;
height: 100%;
background: brown;
transform: rotate(-45deg) translateY(-30%);
transform-origin: left top;
}
}
&:nth-child(4n+2) span {
border-radius: 40%;
}
&:nth-child(8n+3) span {
background: linear-gradient(45deg, #ff6600 0 48%, #333 48% 52%, #fff0 55% 100%);
}
}
Aparte del dibujo básico de las hojas declarado en el span
de cada elemento .leaf
, lo interesante es modificar algunas de las hojas con la pseudo-clase :nth-child()
de la misma manera que hicimos en los copos de nieve con las CSS Entities, pero aquí cambiamos el border-radius
cada 4 elementos empezando por el segundo (4n+2) para redondear más algunas de las hojas, y el dibujo de estas cada 8 hojas empezando por la tercera (8n+3). Los demás cambios se aplican a cada hoja en el bucle for
, donde cambiaremos desde el tamaño de las hojas, hasta el brillo de las mismas, pasando por la velocidad del giro, así como otras propiedades CSS.
Para cambiar la dirección del viento, haciendo :hover
sobre el elemento .direction
que ocupa el 50% de la pantalla y está alineado a la izquierda, aplicamos el siguiente CSS:
.direction:hover ~ .leaf {
animation-direction: reverse !important;
}
Esto hace que la animación cambie de dirección, aunque es un apaño cutre, ya que al cambiar la dirección de la animación lo que ocurre que las hojas dan un salto pues la animación no acaba fuera de la pantalla como si hace por defecto. Para solucionar esto habría que crear una animación nueva y aplicarla en el caso de cambiar la dirección. Cuando saque un rato intentaré mejorarlo…
Burbujas / Bubbles
La cuarta y última semana del reto de Codepen había que crear en efecto de brillo de sol o sunshine, pero me salió un churro (puedes verlo por aquí). De manera que, para acabar, aunque no es un efecto meteorológico, voy a mostrar otro efecto que creé para un Reto de Codepen de Abril de 2024, que usa el mismo principio que los anteriores con PUG y SASS, y en el que solo cambia la dirección de la animación (de abajo hacia arriba) y un efecto de vaivén que no es que esté muy logrado, pero da el pego…
See the Pen Bubbles – CSS by Josetxu (@josetxu) on CodePen.
HTML / PUG
El HTML, como los demás ejemplos, con PUG.
.bubbles
- for (var i=0; i<200; i++ )
.bubble
span
Creamos el elemento contenedor bubbles
.
Dentro de .bubbles
un bucle for
con 200 elementos con clase bubble
.
Y dentro de cada .bubble
un elemento span
para aplicar estilos a las burbujas.
CSS / SASS
En el CSS de las burbujas solo destaco la animación que se crea dentro del bucle específica para cada burbuja.
@keyframes fall-#{$i} {
#{percentage( random(50) / 600 )} {
transform: rotate(90deg) translateX(0);
margin-left: random(25) * 0.1vw;
}
50% {
margin-left: random(25) * 0.1vw;
}
25%, 75% {
margin-left: random(25) * -0.1vw;
}
100% {
transform: rotate(90deg) translateX(calc(100vh + 5vmin));
margin-left: random(25) * 0.1vw;
}
}
Se puede ver el efecto de vaivén aplicando un margin-left
aleatorio, positivo en el 0% , 50% , 100% y negativo en el 25% y el 75%.
Pues eso que con PUG y SASS se pueden hacer verdaderas maravillas.
En próximos artículos mostraré algunos ejemplos más del uso de bucles, para reducir el código empleado (con PUG), y para crear cosas muy chulas como cámaras de visualización 3D (con SASS), algo parecido al efecto que hemos visto para cambiar el ángulo de caída de la lluvia, pero mucho más avanzado.