Inicio Apuntes FPApuntes DAW Malla interactiva de ondas y partículas en tiempo real

Malla interactiva de ondas y partículas en tiempo real

Utiliza la API requestAnimationFrame

Publicado por entreunosyceros

Una vez más aquí. Hoy estoy aquí para dejar un artículo relacionado con el uso de la API requestAnimationFrame con JavaScript, que nos permitirá echar un ojo al mundo de la programación «creativa» y crear una malla interactiva de ondas. En las siguientes líneas vamos a explorar un proyecto realizado con esta API, el cual demuestra algunas cosas fascinantes que se pueden crear con ella.

En el corazón de esta experiencia se encuentra el lenguaje de programación JavaScript. Este lenguaje, ampliamente conocido por su versatilidad y capacidad para interactuar con páginas web, se convierte en una herramienta poderosa cuando se trata de animaciones y gráficos interactivos.

El código de la malla interactiva de ondas

Este proyecto se basa en un código JavaScript que se encarga de generar y manipular una malla de partículas que forman la base de la visualización. Utiliza la API requestAnimationFrame para asegurar una animación fluida y eficiente, proporcionando una experiencia envolvente para el usuario.

gif de ejemplo malla

El código de este ejemplo se estructura en varias secciones que cumplen funciones específicas. Primero, se configuran variables esenciales como el número de cuadrículas por fotograma, el ancho y alto del lienzo, y parámetros de movimiento y velocidad. Luego, se define una clase Part que representa cada partícula en la malla. Cada partícula tiene propiedades como posición, velocidad y referencias a su índice en la cuadrícula.

Todo esto se representa en un archivo HTML, el cual es acompañado por algunas reglas CSS para que todo se vea como debe. A continuación vamos a ver el códigio que compone este ejemplo. Este código se divide en tres archivos, el HTML, otro CSS y el que crea todo el entramado, el archivo JS.

Archivo index.html

Dentro de este archivo será donde se creará el canvas en el que se va a dibujar la malla que se va a representar. Además añadiremos también un texto que se mostrará en el centro de la pantalla.

<!DOCTYPE html>
<html lang="es" >
<head>
  <meta charset="UTF-8">
  <title>Malla Temporal</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="./style.css">

</head>
<body>

<canvas id="canv" width="2265" height="1465"></canvas>
<div class="centered-text" id="texto">entreunosyceros.net</div>

<!-- 
Hacer clic y arrastrar,
hacer clic y mantener pulsado o
hacer clic, mantener pulsado y después arrastrar
-->

  <script  src="./script.js"></script>

</body>
</html>

Archivo style.css

En este archivo colocaremos las pocas reglas CSS necesarias para visualizar correctamente esta malla interactiva de ondas.

body {
  width: 100%;
  overflow: hidden;
  cursor:move;
}

/* Texto */
.centered-text {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 48px;
  font-weight: bold;
  color: white;
  text-shadow: 2px 2px 10px rgba(0, 0, 0, 0.8);
  z-index: 1;
  opacity: 0.3;
}

Archivo script.js

El archivo script.js contiene el código esencial para el funcionamiento de la visualización. Utiliza la API requestAnimationFrame para lograr una animación fluida y eficiente. El código se encarga de calcular y actualizar la posición y velocidad de una serie de partículas que forman una malla.

Las partículas interactúan entre sí y responden a la posición del ratón, lo que permite a los usuarios manipular el comportamiento de la malla. Además, se incorpora una función de onda que crea patrones dinámicos en tiempo real.

// Establece una función para solicitar animación de fotograma
window.requestAnimFrame = (function(callback) {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
    function(callback) {
      window.setTimeout(callback, 1000 / 60);
    };
})();

// Definición de variables
var _x = 2265; // Ancho x (ancho del lienzo)
var _y = 1465; // Alto y (alto del lienzo)
var w = _x / gnum; // Ancho de cuadrícula
var h = _y / gnum; // Alto de cuadrícula
var gnum = 95; // Número de cuadrículas por fotograma
var $; // Contexto del lienzo
var parts; // Partículas
var frm = 0; // Valor de fotograma
var msX = 0; // Coordenada x del ratón
var msY = 0; // Coordenada y del ratón
var msdn = false; // Bandera de clic del ratón
var P1 = 0.0005; // Punto uno
var P2 = 0.01; // Punto dos
var ŭ = 0; // Actualización de color
var n = 0.98; // Valor n para más adelante
var n_vel = 0.03; // Velocidad n

// Definición de la clase Part
var Part = function() {
  this.x = 0; // Posición x
  this.y = 0; // Posición y
  this.ind_x = 0; // Índice en x
  this.ind_y = 0; // Índice en y
  this.vx = 0; // Velocidad en x
  this.vy = 0; // Velocidad en y
};

// Función de fotograma para las partículas
Part.prototype.frame = function() {
  // Si está en el borde de la cuadrícula, no hace nada
  if (this.ind_x == 0 || this.ind_x == gnum - 1 || this.ind_y == 0 || this.ind_y == gnum - 1) {
    return;
  }

  var ax = 0; // Ángulo en x
  var ay = 0; // Ángulo en y
  // off_dx, off_dy = distancia de desplazamiento x, y
  var off_dx = this.ind_x * w - this.x;
  var off_dy = this.ind_y * h - this.y;
  ax = P1 * off_dx;
  ay = P1 * off_dy;

  ax -= P2 * (this.x - parts[this.ind_x - 1][this.ind_y].x);
  ay -= P2 * (this.y - parts[this.ind_x - 1][this.ind_y].y);

  ax -= P2 * (this.x - parts[this.ind_x + 1][this.ind_y].x);
  ay -= P2 * (this.y - parts[this.ind_x + 1][this.ind_y].y);

  ax -= P2 * (this.x - parts[this.ind_x][this.ind_y - 1].x);
  ay -= P2 * (this.y - parts[this.ind_x][this.ind_y - 1].y);

  ax -= P2 * (this.x - parts[this.ind_x][this.ind_y + 1].x);
  ay -= P2 * (this.y - parts[this.ind_x][this.ind_y + 1].y);

  this.vx += (ax - this.vx * n_vel);
  this.vy += (ay - this.vy * n_vel);

  this.x += this.vx * n;
  this.y += this.vy * n;
  if (msdn) {
    var dx = this.x - msX;
    var dy = this.y - msY;
    var ɋ = Math.sqrt(dx * dx + dy * dy);
    if (ɋ < 50) {
      ɋ = ɋ < 10 ? 10 : ɋ;
      this.x -= dx / ɋ * 5;
      this.y -= dy / ɋ * 5;
    }
  }
};

// Función para inicializar las partículas
function go() {
  parts = []; // Array de partículas
  for (var i = 0; i < gnum; i++) {
    parts.push([]);
    for (var j = 0; j < gnum; j++) {
      var p = new Part();
      p.ind_x = i;
      p.ind_y = j;
      p.x = i * w;
      p.y = j * h;
      parts[i][j] = p;
    }
  }
}

// Función para dibujar la cuadrícula
function draw() {
  ŭ += 0.4; // Incrementa el valor de ŭ para cambiar los colores más rápido
  $.strokeStyle = "hsla(" + (ŭ % 360) + ",100%,50%,1)";
  $.beginPath();
  for (var i = 0; i < gnum - 1; i += 1) {
    for (var j = 0; j < gnum - 1; j += 1) {
      var p1 = parts[i][j];
      var p2 = parts[i][j + 1];
      var p3 = parts[i + 1][j + 1];
      var p4 = parts[i + 1][j];
      draw_each(p1, p2, p3, p4);
    }
  }
  $.stroke();
}

// Función para mover las partículas
function mv_part() {
  for (var i = 0; i < gnum; i++) {
    for (var j = 0; j < gnum; j++) {
      var p = parts[i][j];
      p.frame();
    }
  }
}

// Función para dibujar cada elemento en el array
function draw_each(p1, p2, p3, p4) {
  $.moveTo(p1.x, p1.y);
  $.lineTo(p2.x, p2.y);
  $.moveTo(p1.x, p1.y);
  $.lineTo(p4.x, p4.y);

  if (p1.ind_x == gnum - 2) {
    $.moveTo(p3.x, p3.y);
    $.lineTo(p4.x, p4.y);
  }
  if (p1.ind_y == gnum - 2) {
    $.moveTo(p3.x, p3.y);
    $.lineTo(p2.x, p2.y);
  }
}

// Función para llamar a las funciones y ejecutar la animación
function calls() {
  $.fillStyle = "hsla(0, 5%, 5%, .1)";
  $.fillRect(0, 0, _x, _y);

  mv_part();
  draw();
  frm++;
}

// Función para crear el efecto de onda
function wave(x, y) {
  var wv = Math.sin(x / wv * xw);
  var wave = Math.sin(0.2 * w * frm + y * yw) * w;

  return wave;
}

// Inicialización del lienzo y configuración de eventos del ratón
var c = document.getElementById('canv');
var $ = c.getContext('2d');
$.fillStyle = "hsla(0, 5%, 5%, .1)";
$.fillRect(0, 0, _x, _y);

function resize() {
  if (c.width < window.innerWidth) {
    c.width = window.innerWidth;
  }

  if (c.height < window.innerHeight) {
    c.height = window.innerHeight;
  }
}

// Llamada a la función go al cargar la página
window.requestAnimFrame(go);

// Event listeners para el ratón
document.addEventListener('mousemove', MSMV, false);
document.addEventListener('mousedown', MSDN, false);
document.addEventListener('mouseup', MSUP, false);

// Se activa cuando se presiona el botón del ratón
function MSDN(e) {
  msdn = true;
}

// Se activa cuando se mueve el ratón. Calcula la posición del ratón dentro del lienzo (msX y msY) en relación con el área del lienzo y el evento del ratón (e)
function MSMV(e) {
  var rect = e.target.getBoundingClientRect();
  msX = e.clientX - rect.left;
  msY = e.clientY - rect.top;
}

// Se activa cuando se libera el botón del ratón
function MSUP(e) {
  msdn = false;
}

// Función para ejecutar la animación
window.onload = function() {
  run();

  function run() {
    window.requestAnimFrame(calls);
    window.requestAnimFrame(run, 33);
  }
  resize();
};

Características generales

Lo que hace de este ejemplo algo especial es su nivel de interactividad. Los usuarios tienen la capacidad de influir directamente en el comportamiento de las partículas a través de su interacción con el ratón. Al hacer clic y arrastrar, pueden observar cómo las partículas reaccionan y se desplazan en consecuencia. Esto brinda una sensación de control directo sobre la experiencia visual, convirtiendo al espectador en parte integral de la obra de arte en constante evolución.

Una característica destacada de este proyecto es la capacidad de aplicar fuerzas adicionales a las partículas. Mantener presionado el botón del ratón introduce una fuerza adicional en el sistema, permitiendo una mayor manipulación y creación de patrones visuales únicos. Este juego de fuerzas crea una experiencia dinámica y cautivadora que va más allá de una simple observación pasiva.

Otro componente clave de esta visualización es la introducción de una función de onda. Esto agrega un elemento adicional de dinamismo a la experiencia. Las ondas viajan a través de la malla, creando patrones cambiantes y fluidos que aportan una sensación de movimiento y vida a la escena.

Este proyecto no solo ofrece una experiencia visual interesante, sino que también invita a los usuarios a explorar su propia creatividad. La combinación de interactividad y opciones de personalización vía código, permite a los usuarios experimentar y crear sus propios patrones visuales únicos.

Para terminar, solo queda decir que este ejemplo también se puede descargar desde el repositorio en GitHub en el que lo he alojado. Además este proyecto se puede probar en el siguiente enlace.

También te puede interesar ...

Deja un comentario

* Al utilizar este formulario, aceptas que este sitio web almacene y maneje tus datos.

Adblock Detectado!!

Ayúdanos deshabilitando la extensión AdBlocker de tu navegador para visitar esta web.
Si no sabes hacerlo en Chrome, consulta el siguiente enlace. Si utilizas Firefox, puedes consultar este otro enlace.
Esto mejorará tu experiencia en este sitio web.