Dejar rastro

facebook-svg gplus-svg twitter-svg

Supongamos que tenemos una partícula que sigue un movimiento cualquiera. En este caso he optado por un movimiento pseudo-aleatorio basado en el momento actual, pero habría podido ser cualquier otro tipo de trayectoria:

var momento = new Date().getTime()/30;

La función que actualiza la posición de la partícula utiliza el momento actual para generar un movimiento pseudo-aleatorio.

this.actualizar = function(momento,frames){//23, 29, 31, 37 números primos!
this.x = c.x+Math.cos(momento/23+Math.cos(momento/29+frames*rad))*(c.x -2*this.r);
this.y = c.y+Math.sin(momento/31+Math.cos(momento/37+frames*rad))*(c.y -2*this.r);
}

Ya lo se, esta función parece muy complicada pero no es imprescindible entenderla para comprender lo que sigue. Lo que sí que hay que tener claro es que esta función actualiza, fotograma con fotograma, las coordenadas ( this.x y this.y ) de la partícula.
Aparte de esto lo demás no tiene nada de especial: la función Particula() crea una nueva particula.


function Particula() {
  this.x = -this.r;
  this.y = -this.r;
  this.r = 10;

  this.dibujar = function() {
    ctx.fillStyle = "#6ab150";
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
    ctx.fill();
  }

  this.actualizar = function(momento, frames) { //53, 59, 61, 67 números primos!
    this.x = c.x + Math.cos(momento / 23 + Math.cos(momento / 29 + frames * rad)) * (c.x - 2 * this.r);
    this.y = c.y + Math.sin(momento / 31 + Math.cos(momento / 37 + frames * rad)) * (c.y - 2 * this.r);
  }
}

y la función Animar() borra el canvas con el método clearRect(), actualiza la posición (  particula.actualizar()  ) y finalmente dibuja de nuevo la particula en la nueva posición ( particula.dibujar() )


function Animar() {
    elId = window.requestAnimationFrame(Animar);
    frames += .25;
    ctx.clearRect(0, 0, cw, ch);
    var momento = new Date().getTime() / 30;
    particula.actualizar(momento, frames);
    particula.dibujar();
}

Veamos el código:

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

Dejando rastro con transparencias

Si queremos que la partícula deje un rastro en el canvas podemos simplemente utilizar fillRect() en lugar de clearRect(), y rellenar el canvas con un color semitransparente ( rgba(0,0,0,.5) )

function Animar() {
    elId = window.requestAnimationFrame(Animar);
    frames += .25;
    ctx.clearRect(0, 0, cw, ch);
    ctx.fillStyle = "rgba(0,0,0, .05)";
    ctx.fillRect(0, 0, cw, ch);
    momento = new Date().getTime() / 30;
    particula.actualizar(momento, frames);
    particula.dibujar();
  }

En función del valor del componente alpha del color, el rastro será más largo ( para valores más pequeños ) o más corto ( para valores más grandes ). En el siguiente ejemplo el valor del componente alpha es .05.

ctx.fillStyle = "rgba(0,0,0, .05)";

See the Pen dejando rastro #1 by Gabi (@enxaneta) on CodePen.

El historial del movimiento

Pero esto no nos sirve de mucho, y si queremos hacer algo más interesante tenemos que guardar el historial del movimiento de la partícula en un array.

this.historial = [];
  . . . . .
  var posicion = {
        x: this.x,
        y: this.y
      }
  this.historial.push(posicion);

No queremos que el historial sea infinito, por lo cual tenemos que definir la longitud máxima del historial, en este caso:

this.maxLongitudHistorial = this.r;

Si el historial es más largo que this.maxLongitudHistorial tenemos que eliminar los elementos que sobran. Recuerde que para popular el historial hemos utilizado el método push(), que añade nuevos elementos al final del array. En este caso tenemos que eliminar los elementos situados al inicio del array, en la posición [0] y para esto utilizamos el método splice():

this.historial.splice(0, 1);
El nuevo método actualizar

Ahora la función que actualiza la posición de la partícula, también guarda la posición actual en un objeto:

var posicion = {
    x: this.x,
    y: this.y
}

y añade el nuevo objeto al final del array historial.

this.historial.push(posicion);

Si el historial es más largo que this.maxLongitudHistorial

if (this.historial.length > this.maxLongitudHistorial) {

elimina un elemento situado al inicio del array

this.historial.splice(0, 1);

this.actualizar = function(momento, frames) { //23, 29, 31, 37 números primos!
    this.x = c.x + Math.cos(momento / 23 + Math.cos(momento / 29 + frames * rad)) * (c.x - 2 * this.r);
    this.y = c.y + Math.sin(momento / 31 + Math.cos(momento / 37 + frames * rad)) * (c.y - 2 * this.r);
    // guarda la posición actual en un objeto
    var posicion = {
      x: this.x,
      y: this.y
    }
    // añade un nuevo objeto al final del array historial
    this.historial.push(posicion);
    // si el historial es más largo que this.maxLongitudHistorial tenemos que eliminar los elementos que sobran. 
    if (this.historial.length > this.maxLongitudHistorial) {
      // elimina un elemento situado al inicio del array
      this.historial.splice(0, 1)
    }
  }
}

También tenemos que actualizar el método que dibuja la particula... y "el rastro". Para esto utilizamos un bucle for que recorre el historial y dibuja uno a uno todos los arcos "fantasma". El radio de estos arcos es 1+i, donde i representa el índex del array historial. O sea: el primer arco es el más pequeño (0 + 1), mientras que el último arco, el actual, es el más grande.

for (var i = 0; i < this.historial.length; i++) {
        var pos = this.historial[i];
        ctx.beginPath();
        ctx.arc(pos.x, pos.y, 1 + i, 0, 2 * Math.PI);
        ctx.fill();
}

this.dibujar = function() {
  ctx.fillStyle = "#6ab150";
  ctx.beginPath();
  ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
  ctx.fill();
  //Para cada posición del historial dibuja un nuevo circulo cuyo radio es 1+i
  for (var i = 0; i < this.historial.length; i++) {
  var pos = this.historial[i];
  ctx.beginPath();
  ctx.arc(pos.x, pos.y, 1 + i, 0, 2 * Math.PI);
  ctx.fill();
  }
}

See the Pen dejando rastro #2 by Gabi (@enxaneta) on CodePen.