118 lines
2.8 KiB
JavaScript
118 lines
2.8 KiB
JavaScript
|
const IMGS = [
|
||
|
"/img/rains/arch.png",
|
||
|
"/img/rains/bmo.png",
|
||
|
"/img/rains/cat.gif",
|
||
|
"/img/rains/demi.png",
|
||
|
"/img/rains/dnd.png",
|
||
|
"/img/rains/eattherich.png",
|
||
|
"/img/rains/gloomhaven.png",
|
||
|
"/img/rains/iron_throne.png",
|
||
|
"/img/rains/link.png",
|
||
|
"/img/rains/meddle.jpeg",
|
||
|
"/img/rains/moon.png",
|
||
|
"/img/rains/note.png",
|
||
|
"/img/rains/obsidian.png",
|
||
|
"/img/rains/outerwilds.png",
|
||
|
"/img/rains/piano.png",
|
||
|
"/img/rains/pirate.svg",
|
||
|
"/img/rains/ror.png",
|
||
|
"/img/rains/smashball.svg",
|
||
|
"/img/rains/ssa.png",
|
||
|
"/img/rains/svelte.png",
|
||
|
"/img/rains/usu.png",
|
||
|
];
|
||
|
|
||
|
const DROPLETS = 8;
|
||
|
const DROPLET_WIDTH_RANGE = [80, 120];
|
||
|
const DROPLET_DY_RANGE = [0.1, 0.4];
|
||
|
const DROPLET_DTHETA_RANGE = [-0.25, 0.25];
|
||
|
const DROPLET_INIT_THETA_RANGE = [-180, 180];
|
||
|
|
||
|
const makeDroplet = (src, width, x, y, theta, dtheta, dy) => {
|
||
|
const img = document.createElement("img");
|
||
|
img.style.position = "absolute";
|
||
|
img.style.width = `${width}px`;
|
||
|
img.src = src;
|
||
|
document.body.appendChild(img);
|
||
|
|
||
|
return {
|
||
|
img,
|
||
|
width,
|
||
|
theta,
|
||
|
pos: { x, y },
|
||
|
vel: { dy, dtheta },
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const randBetween = (min, max) => Math.random() * (max - min) + min;
|
||
|
|
||
|
const update = (droplets, dt, boundedY) =>
|
||
|
droplets
|
||
|
.map((droplet) => {
|
||
|
return {
|
||
|
...droplet,
|
||
|
pos: { ...droplet.pos, y: droplet.pos.y + droplet.vel.dy * dt },
|
||
|
theta: droplet.theta + droplet.vel.dtheta * dt,
|
||
|
};
|
||
|
})
|
||
|
.map((droplet) => {
|
||
|
const {
|
||
|
pos: { y },
|
||
|
img,
|
||
|
} = droplet;
|
||
|
if (y >= boundedY) {
|
||
|
document.body.removeChild(img);
|
||
|
return null;
|
||
|
}
|
||
|
return droplet;
|
||
|
})
|
||
|
.filter((x) => x)
|
||
|
.map((droplet) => {
|
||
|
const {
|
||
|
img,
|
||
|
pos: { x, y },
|
||
|
theta,
|
||
|
} = droplet;
|
||
|
|
||
|
img.style.left = `${x}px`;
|
||
|
img.style.top = `${y}px`;
|
||
|
img.style.transform = `rotate(${theta}deg)`;
|
||
|
|
||
|
return droplet;
|
||
|
});
|
||
|
|
||
|
const buildRandomDroplet = () =>
|
||
|
makeDroplet(
|
||
|
IMGS[Math.floor(randBetween(0, IMGS.length))],
|
||
|
randBetween(...DROPLET_WIDTH_RANGE),
|
||
|
randBetween(0, window.innerWidth - DROPLET_WIDTH_RANGE[1]),
|
||
|
randBetween(-50, window.innerHeight / 8),
|
||
|
randBetween(...DROPLET_INIT_THETA_RANGE),
|
||
|
randBetween(...DROPLET_DTHETA_RANGE),
|
||
|
randBetween(...DROPLET_DY_RANGE),
|
||
|
);
|
||
|
|
||
|
window.onload = () => {
|
||
|
// cache 'em
|
||
|
IMGS.forEach((x) => {
|
||
|
((img) => (img.src = x))(new Image());
|
||
|
});
|
||
|
|
||
|
let droplets = Array(DROPLETS).fill().map(buildRandomDroplet);
|
||
|
|
||
|
let lastTimestamp = performance.now();
|
||
|
const next = (timestamp) => {
|
||
|
const dt = timestamp - lastTimestamp;
|
||
|
lastTimestamp = timestamp;
|
||
|
|
||
|
droplets = update(droplets, dt, window.innerHeight);
|
||
|
|
||
|
if (droplets.length != DROPLETS) {
|
||
|
droplets.push(buildRandomDroplet());
|
||
|
}
|
||
|
|
||
|
requestAnimationFrame(next);
|
||
|
};
|
||
|
requestAnimationFrame(next);
|
||
|
};
|