30 – Como hacer una blob animado en HTML - Parte IV

Posted on Sep 19, 2021 – - by Franco
Cover del post

Bienvenidos a la cuarta parte de nuestro tutorial sobre como crear un blob animado usando solo Canvas API. Si te perdiste las partes anteriores puedes encontrarlas a continuación:

  • Parte I: Aprenderemos a utilizar el API de HTML Canvas para dibujar formas primitivas. link
  • Parte II: Haremos un pequeño anillo 3D proyectado en 2 dimensiones. link
  • Parte III: Haremos una Esfera completa y le añadiremos rotación en los tres ejes. link
  • Parte IV: Distorsionaremos la esfera para hacerla parecer un blob. Este
  • Parte V: Le agregaremos interacciones con el mouse. (Próximamente)
  • Parte VI: Le agregaremos controles interactivos para ‘jugar’ con los parametros de nuestra esfera. (Próximamente)

Recapitulación:

Esto es lo que tenemos hasta ahora, una esfera de un solo color rotando en los tres ejes.

En el tutorial de hoy vamos a agregar lo que se conoce como un ruido, en específico usaremos un ruido Simplex. Elegimos este tipo de ruido por su menor complejidad computacional y sus buenos resultados.

JWagner nos ofrece este ruido en un paquete de NPM disponible en Github.

Para añadirlo a nuestro proyecto solo tenemos que añadir el link al CDN público.

1
<script src="https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.4.0/simplex-noise.min.js"></script>

Para usarlo solo tenemos que crear una instancia de Simplex y luego elegir las dimensiones y las coordenadas donde queremos obtener el ruido. En este caso usaremos el ruido en 3D (noise3D), tomando como referencia los ejes X, Y y Z.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 1. Instanciamos SimplexNoise
let simplex = new SimplexNoise();
// 2. Obtenemos el tiempo actual desde 1970 hasta ahora en milisegundos y lo dividimos por 5000;
const time = Date.now() / 5000;
// 3. Obtenemos la magnitud actual del punto que vamos a modificar.
const magnitude = Math.sqrt(x*x + y*y + z*z)
// 4. Un multiplicador para exagerar la deformación.
const spikes = 1;
// 5. Obtenemos el desplazamiento para las coordenadas
let displacement = simplex.noise3D(x/magnitude * spikes, // eje X
											  y/magnitude * spikes, // eje Y
											  z/magnitude * spikes + time) // Eje Z

// 6. Variable para reducir el impacto de la deformación.
const dampening = 0.3;
// 7. Multiplicamos cada variable por el desplazamiento
x = x * (1 + dampening * displacement);
y = y * (1 + dampening * displacement);
z = z * (1 + dampening * displacement);

Vayamos linea por linea

  1. Instanciamos SimplexNoise en la variable simplex.
  2. Obtenemos el tiempo actual desde el 1 de enero de 1970 en milisegundos (esto se llama tiempo UNIX, algo común en el mundo de la programación) y lo dividimos por (5000 para que varie cada 5 segundos)
  3. Obtenemos la magnitud del punto que queremos modificar. Esto se logra al sacar la raiz cuadrada de la suma de los cuadrados de las coordenadas. Esto es importante porque SimplexNoise espera un valor entre (0 y 1)
  4. Una constante el cual usaremos para exagerar el ruido generado.
  5. Obtenemos el desplazamiento usando el metodo noise3D (porque estamos usando 3 dimensiones).
  6. Declaramos una variable dampening para reducir el efecto del desplazamiento obtenido en 5.
  7. Multiplicamos entonces el valor obtenido (entre 1 y 2) por el valor original en cada uno de los 3 ejes.

Con eso ya está resuelto el desplazamiento random de nuestro blob, veamos como integrarlo:

Crearemos una funcion addNoiseToPoint() para aplicarle este código que hemos generado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. Instanciamos SimplexNoise una sola vez
let simplex = new SimplexNoise();

function addNoiseToPoint(point){
	// 2. Obtenemos el tiempo actual desde 1970 hasta ahora en milisegundos y lo dividimos por 5000;
	const time = Date.now() / 5000;
	// 3. Obtenemos la magnitud actual del punto que vamos a modificar.
	const magnitude = Math.sqrt(point.x*point.x + point.y*point.y + point.z*point.z)
	// 4. Un multiplicador para exagerar la deformación.
	const spikes = 0.6;
	// 5. Obtenemos el desplazamiento para las coordenadas
	let displacement = simplex.noise3D(point.x/magnitude * spikes, // eje X
												point.y/magnitude * spikes, // eje Y
												point.z/magnitude * spikes + time) // Eje Z

	// 6. Variable para reducir el impacto de la deformación.
	const dampening = 0.3;
	// 7. Multiplicamos cada variable por el desplazamiento
	const x = point.x * (1 + dampening * displacement);
	const y = point.y * (1 + dampening * displacement);
	const z = point.z * (1 + dampening * displacement);
	return {x,y,z};
}

Este es el resultado:

Este post dominguero es algo pequeño, pero vamos a agregarle un poco de estilo en el <head> para agregarle un poco de estilo a nuestro blob.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
	<style>
	/* Estilos */
	html,body {
		height: 100%;
	}

	body {
		text-align: center;
		background: #000;
		background: linear-gradient(#141E30, #243B55);
		/* System Fonts as used by GitHub */
		font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
	}
	
	h1, h2, #x {
		font-size: 8rem;
		text-align: center;
		margin-top: 15%;
		top: 0;
		left: 25%;
		width: 50%;
		color:white;
		position: fixed;
		font-family: 'Metropolis', arial; line-height: 7rem;
		letter-spacing: -0.8rem;
	}

	h2{
		margin-top: 25%;
		font-size: 6rem;
	}

	#x{
		font-weight: 300;
		opacity: 0.5;
		margin-top: 20%;
		font-size: 3rem;
	}
	canvas{
		margin-top: 5rem;
	}
	</style>

Y dentro de la función drawPoint() le agregaremos esto luego de la modificación de radio:

1
2
3
// ...
radius = radius + (point.z/20);
color = `hsla(212, 40%, 50%, ${radius/10})`; // Agregamos esto.

Perfecto, ahora ya está casi listo nuestro blob. En el siguiente post agregaremos un panel para refinar todas las constantes que hemos declarado en nuestro recorrido y refactorizaremos nuestro código para hacerlo más legible.

¡Hasta la próxima parte!


Código completo de esta parte: