Conectar particulas

facebook-svg gplus-svg twitter-svg

Conectar partículas en movimiento es un tipo de animación que se ve mucho por ahí. Veamos como hacerlo.

En el HTML tenemos un elemento canvas, el lienzo donde queremos animar las partículas.

<canvas></canvas>

En el JavaScript primero iniciamos el canvas:


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

Las variables cw y ch representan, como es fácil de ver, la anchura y respectivamente la altura del canvas.

Algunas variables útiles

A continuación queremos animar 60 partículas que rebotan en las paredes del canvas.

var numParticulas = 60;

y se conectan si la distancia entre ellas es igual o más pequeña que maxDist.

var maxDist = 50;

El color de las partículas y de los conectores es verde.

ctx.fillStyle = "#6ab150";
ctx.strokeStyle = "#6ab150";

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

var particulas = [];

El constructor

Primero construimos una función que nos ayude encontrar un número aleatorio entre dos valores minim y maxim.

function randomIntFromInterval(minim, maxim) {
   return ~~(Math.random() * (maxim - minim + 1) + minim);
}

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

Ahora podemos escribir la función Particula(), de hecho un constructor que establece el radio de cada partícula.

this.r = 3;

También establece las coordenadas en x e y de estas: un punto del canvas escogido al azar.

this.x = randomIntFromInterval(this.r, cw - this.r);
this.y = randomIntFromInterval(this.r, ch - this.r);

La velocidad inicial de cada partícula es un número aleatorio entre -3 y 3:

this.vx = Math.random() * 6 - 3; 
this.vy = Math.random() * 6 – 3;

function Particula() {
      this.r = 3;
      this.x = randomIntFromInterval(this.r, cw - this.r);
      this.y = randomIntFromInterval(this.r, ch - this.r);
      this.vx = Math.random() * 6 - 3; // un número aleatorio entre -3 y 3
      this.vy = Math.random() * 6 - 3; // un número aleatorio entre -3 y 3
    }

Dibujar

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


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

La función conectar

Dados dos puntos p1 y p2, la función calcula la distancia entre estos dos puntos,

var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var dist = Math.abs(Math.sqrt(dx * dx + dy * dy))

Si la distancia es mas pequeña que maxDist conecta los dos puntos con una línea.

if (dist < maxDist) { 
    ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.stroke();
}

function conectar(p1, p2) {
      var dx = p2.x - p1.x;
      var dy = p2.y - p1.y;
      var dist = Math.abs(Math.sqrt(dx * dx + dy * dy));
      if (dist < maxDist) { 
        ctx.beginPath();
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.stroke();
      }
}

El array de puntos

A continuación creamos los puntos que queremos animar y los guardamos en el array particulas.

for (var i = 0; i < numParticulas; i++) {
      particulas.push(new Particula());
}

El movimiento

Para mover las partículas sumamos al valor de las coordenadas el valor de la velocidad: p.vx o p.vy.

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

Si una partícula empieza a salirse del canvas, rebota: o sea cambia de dirección. Para que esto pase solo hace falta multiplicar la velocidad de la particula con -1. Por ejemplo: p.vx *= -1;

Finalmente viene la parte importante: tenemos que verificar la distancia que existe entre el punto p y todas las demás partículas. Si utilizamos un bucle for de tipo for( var j = 0; j < particulas.length; j++), no solo verificamos la distancia entre este punto y las demás partículas, sino que verificamos la distancia dos veces, además de verificar la distancia consigo mismo: un derroche inútil! Para que esto no pase el bucle for tiene que tener este aspecto:

for (var j = i + 1; j < particulas.length; j++) {
        conectar(p, particulas[j]);
}

function mover(p, i) {
      p.x += p.vx;
      p.y += p.vy;
      // si tocan los bordes del canvas rebotan
      if (p.x > cw - p.r) {
        p.vx *= -1;
      } else if (p.x < p.r) {
        p.vx *= -1;
      }
      if (p.y > ch - p.r) {
        p.vy *= -1;
      } else if (p.y < p.r) {
        p.vy *= -1;
      }
     
      for (var j = i + 1; j < particulas.length; j++) {
        conectar(p, particulas[j]);
      }
    }

La animación

Hasta ahora lo hemos preparado todo, ahorra podemos animar las partículas.
La función Animar() es una función recursiva, o sea una función que se llama a si misma:

elId = window.requestAnimationFrame(Animar);

A continuación limpiamos el canvas.

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

Y utilizamos un bucle for para mover y dibujar cada partícula.

for (var i = 0; i < particulas.length; i++) {
     mover(particulas[i], i);
     particulas[i].dibujar();
}

Y ya está.


function Animar() {
      elId = window.requestAnimationFrame(Animar);
      ctx.clearRect(0, 0, cw, ch);
      for (var i = 0; i < particulas.length; i++) {
        mover(particulas[i], i);
        particulas[i].dibujar();
      }
    }
    elId = window.requestAnimationFrame(Animar);
    

El resultado

Es recomendable abrirlo y manosearlo en codepen.io

See the Pen conexiones by Gabi (@enxaneta) on CodePen.