110 lines
2.7 KiB
JavaScript
110 lines
2.7 KiB
JavaScript
/*
|
|
Johan Karlsson, 2019
|
|
https://twitter.com/DonKarlssonSan
|
|
MIT License, see Details View
|
|
*/
|
|
|
|
class Particle {
|
|
constructor() {
|
|
this.x = Math.random() * w;
|
|
this.y = Math.random() * h;
|
|
this.angle = Math.random() * Math.PI * 2;
|
|
}
|
|
|
|
move() {
|
|
let zoom = 70;
|
|
let stepSize = 0.5;
|
|
let x = Math.floor(this.x);
|
|
let y = Math.floor(this.y);
|
|
let index = y * w + x;
|
|
let isInside = index < imageBuffer.length && imageBuffer[index];
|
|
if (isInside) {
|
|
// This is cheating in the name of performance.
|
|
// In a real flow field the strength varies over
|
|
// the area of the field. Here we hard code it.
|
|
// The visual result is acceptable
|
|
//let strength = simplex.noise3D(this.x / zoom, this.y / zoom, ticker) * 4;
|
|
let strength = 1;
|
|
let xn = simplex.noise3D(this.x / zoom, this.y / zoom, ticker + 4000) * strength;
|
|
let yn = simplex.noise3D(this.x / zoom, this.y / zoom, ticker + 8000) * strength;
|
|
this.x += xn;
|
|
this.y += yn;
|
|
} else {
|
|
this.angle += (Math.random() - 0.5) * 0.5;
|
|
this.x += Math.cos(this.angle) * stepSize;
|
|
this.y += Math.sin(this.angle) * stepSize;
|
|
}
|
|
|
|
if (this.x < 0) this.x = w;
|
|
if (this.x > w) this.x = 0;
|
|
if (this.y < 0) this.y = h;
|
|
if (this.y > h) this.y = 0;
|
|
}
|
|
|
|
draw() {
|
|
ctx.fillRect(this.x, this.y, 1, 1);
|
|
}}
|
|
|
|
|
|
let canvas;
|
|
let ctx;
|
|
let w, h;
|
|
let imageBuffer;
|
|
let particles;
|
|
let ticker;
|
|
let simplex;
|
|
|
|
function setup() {
|
|
ticker = 0;
|
|
simplex = new SimplexNoise();
|
|
canvas = document.querySelector("#canvas");
|
|
ctx = canvas.getContext("2d");
|
|
reset();
|
|
window.addEventListener("resize", reset);
|
|
}
|
|
|
|
function setupParticles() {
|
|
particles = [];
|
|
let nrOfParticles = w * h / 40;
|
|
for (let i = 0; i < nrOfParticles; i++) {
|
|
particles.push(new Particle());
|
|
}
|
|
}
|
|
|
|
function reset() {
|
|
w = canvas.width = window.innerWidth;
|
|
h = canvas.height = window.innerHeight;
|
|
storeHeartInBuffer();
|
|
setupParticles();
|
|
ctx.fillRect(0, 0, w, h);
|
|
}
|
|
|
|
function draw() {
|
|
requestAnimationFrame(draw);
|
|
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
|
|
ctx.fillRect(0, 0, w, h);
|
|
ctx.fillStyle = "red";
|
|
particles.forEach(p => {
|
|
p.move();
|
|
p.draw();
|
|
});
|
|
ticker += 0.014;
|
|
}
|
|
|
|
function storeHeartInBuffer() {
|
|
ctx.beginPath();
|
|
for (let angle = 0; angle < Math.PI * 2; angle += 0.01) {
|
|
let r = Math.min(w, h) * 0.025;
|
|
let x = r * 16 * Math.pow(Math.sin(angle), 3);
|
|
let y = -r * (13 * Math.cos(angle) - 5 * Math.cos(2 * angle) - 2 * Math.cos(3 * angle) - Math.cos(4 * angle));
|
|
ctx.lineTo(w / 2 + x, h * 0.45 + y);
|
|
}
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
let image = ctx.getImageData(0, 0, w, h);
|
|
imageBuffer = new Uint32Array(image.data.buffer);
|
|
}
|
|
|
|
setup();
|
|
draw(); |