diff options
Diffstat (limited to 'site/main.mjs')
| -rw-r--r-- | site/main.mjs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/site/main.mjs b/site/main.mjs new file mode 100644 index 0000000..0c81b41 --- /dev/null +++ b/site/main.mjs @@ -0,0 +1,140 @@ +const NUM_POINTS = 5; + +// thanks vihart +const TAU = 2 * Math.PI; + +// no more than 1% of world size per tick. +const MAX_SPEED = 0.01; + +// size of rendered points in proportion to canvas. +const POINT_RADIUS = 0.01; + +class Point { + x = Math.random(); + y = Math.random(); + #heading = Math.random() * TAU; + speed = Math.random() * MAX_SPEED; + color = randColor(); + + #speedX; + get speedX() { + if (this.#speedX === undefined) { + this.#speedX = this.speed * Math.cos(this.heading); + } + return this.#speedX; + } + + #speedY; + get speedY() { + if (this.#speedY === undefined) { + this.#speedY = this.speed * Math.sin(this.heading); + } + return this.#speedY; + } + + get heading() { + return this.#heading; + } + + set heading(val) { + this.#heading = val; + this.#speedX = this.#speedY = undefined; + } +} + +const points = [...Array(NUM_POINTS)].map(_ => new Point()); + +function randColor() { + const [r, g, b] = [...Array(3)].map(_ => Math.random() * 255); + return `rgb(${r} ${g} ${b})` +} + +function renderPoints(ctx, points) { + points.forEach(p => { + ctx.fillStyle = p.color; + ctx.beginPath(); + ctx.arc(p.x, p.y, POINT_RADIUS, 0, TAU); + ctx.fill(); + + ctx.lineWidth = 0.005; + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + ctx.lineTo(p.x + p.speedX * 3, p.y + p.speedY * 3); + ctx.stroke(); + }); +} + +function movePoints(points) { + points.forEach(p => { + p.x += p.speedX; + p.y += p.speedY; + }); +} + +function bouncePoints(points) { + let didBounce = false; + points.forEach(p => { + const x = p.x + p.speedX; + const y = p.y + p.speedY; + + if (x < 0) { + p.heading = Math.PI - p.heading; + didBounce = true; + } else if (x >= 1) { + p.heading = Math.PI - p.heading; + didBounce = true; + } else if (y < 0) { + p.heading = -1 * p.heading; + didBounce = true; + } else if (y >= 1) { + p.heading = -1 * p.heading; + didBounce = true; + } + }); + return didBounce; +} + +async function loaded() { + const canvas = document.querySelector('canvas'); + const ctx = canvas.getContext('2d'); + console.debug('canvas:', canvas, 'ctx', ctx, 'points:', points); + ctx.scale(canvas.width, canvas.height); + + const fps = document.querySelector('#fps'); + + const goButton = document.querySelector('button'); + let paused = true; + goButton.onclick = e => { + paused = !paused; + if (paused) { + e.target.textContent = 'go'; + } else { + e.target.textContent = 'pause'; + self.requestAnimationFrame(render); + } + }; + + let lastTime = document.timeline.currentTime; + function render(t) { + if (t > lastTime) { + fps.textContent = Math.floor(1_000 / (t - lastTime)); + lastTime = t; + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + renderPoints(ctx, points); + if (bouncePoints(points)) { + //goButton.onclick({ target: goButton }); + } + movePoints(points); + + if (!paused) { + self.requestAnimationFrame(render); + } + } + + //goButton.onclick({ target: goButton }); + render(document.timeline.currentTime); +} + +document.addEventListener('DOMContentLoaded', loaded); |
