Copos de nieve

facebook-svg gplus-svg twitter-svg

Crear copos de nieve que caen constantemente es algo muy fácil.
En el HTML tenemos un elemento canvas, el lienzo donde queremos animar los copos de nieve.

<canvas></canvas>

En el JavaScript primero iniciamos el canvas:


		 var canvas = document.querySelector("canvas");
		 var ctx = canvas.getContext("2d");
         var cw = canvas.width = window.innerWidth;
		 var ch = canvas.height = window.innerHeight;
         

Las variables cw y ch representan, como es fácil de ver, la anchura y respectivamente la altura del canvas. Utilizamos window.innerWidth y window.innerHeight porque queremos que el canvas cubra toda la ventana.

Algunas variables útiles

A continuación queremos animar 300 copitos de nieve que caen lentamente del cielo.

var numCopos = 300;

El color de los copos es blanco

ctx.fillStyle = "white";

Cuando trabajamos con partículas (en este caso copitos de nieve) en canvas es importante crear un array donde guardar todas las características de cada partícula en parte.

var coposRy = [];

El constructor

En el JavaScript creamos una función Copo() que  construye una a una las partículas. En este caso las partículas son pequeños círculos cuyo radio r, varia entre 1px y 3px.

~~(Math.random()*3 + 1);

Para quien no lo sepa, en JavaScript, la doble tilde (~~) es un operador equivalente a Math.floor() o casi.

Mientras que el valor de la coordenada en x de cada partícula las sitúa repartidas aleatoriamente a lo ancho del canvas

~~(Math.random()*cw + 1);

el valor de la coordenada en y de cada partícula en el momento de la creación las sitúa fuera del canvas, justo encima de este.

this.y = -this.r;

La función Copo() también establece la velocidad inicial de cada partícula:
this.vy , la caída, es un número integro aleatorio, entre 1 y 3.

this.vy =  ~~(Math.random()*3 + 1);

Queremos que la velocidad en x tome valores entre .5 y 1.5

this.vx = ~~(Math.random() * (15 - 5 + 1) + 5)/10;

Pero esto generaría un movimiento de las partículas solo hacia la derecha. Para que esto no pase lo multiplicamos aleatoriamente con 1 o -1:

~~(Math.random() * (15 - 5 + 1) + 5)/10 *  (Math.random() < .5 ? 1 : -1);

function Copo() {
    this.r = ~~(Math.random()*3 + 1);// el radio
    this.x = ~~(Math.random()*cw + 1);
    this.y = -this.r;
    this.vx = ~~(Math.random() * (15 - 5 + 1) + 5)/10 * (Math.random() < .5 ? 1 : -1);
    this.vx =~~(Math.random()*3 + 1);
}

Dibujar

Para poder dibujar fácilmente cada copito, extendemos la funcionalidad del constructor de esta manera: (el código es auto explicativo).


Copo.prototype.dibujar = function(ctx) {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
    ctx.fill();
}

El movimiento

Si los copos se salen del canvas,

if (this.x >= cw + this.r ||
   this.x <= -this.r ||
   this.y >= ch + this.r
)

los podemos reutilizar, colocándolos de nuevo en el punto de partida.

this.y = -this.r;
this.x = ~~(Math.random()*cw + 1);

De lo contrario, sumamos al valor de las coordenadas de cada partícula, el valor de la velocidad correspondiente.

this.x += this.vx;
this.y += this.vy;

Copo.prototype.fueraDelCanvas = function() {
    if (this.x >= cw + this.r ||
      this.x <= -this.r ||
      this.y >= ch + this.r
    ) {
      this.y = -this.r;
      this.x = ~~(Math.random()*cw + 1);
    } else {
      this.x += this.vx;
      this.y += this.vy;
    }
  }

La animación

Hasta ahora lo hemos preparado todo, pero todavía no hay ningún copito de nieve en el array coposRy. Los añadimos uno a uno en cada fotograma, hasta llegar al número máximo de copitos de nieve establecido (numCopos).

if (coposRy.length < numCopos) {
   var copo = new Copo();
   coposRy.push(copo);
}

Limpiamos el canvas utilizando el método clearRect().

ctx.clearRect(0, 0, cw, ch);

Después, un bucle for mueve y dibuja uno a uno todos los copos de nieve del array

for (var i = 0; i < coposRy.length; i++) {
      coposRy[i]. movimiento();
      coposRy[i].dibujar(ctx);
}

function AnimarCopos() {
    el_Id = window.requestAnimationFrame(AnimarCopos);
    if (coposRy.length < numCopos) {
      var copo = new Copo();
      coposRy.push(copo);
    }
    ctx.clearRect(0, 0, cw, ch);
  
    for (var i = 0; i < coposRy.length; i++) {
      coposRy[i]. movimiento();
      coposRy[i].dibujar(ctx);
    }
  }
  
  el_Id = window.requestAnimationFrame(AnimarCopos);

El resultado

Es recomendable abrirlo y manosearlo en codepen.io

See the Pen Nevada de partículas* by Gabi (@enxaneta) on CodePen.