diff options
| author | brian cully <bjc@spork.org> | 2025-12-23 21:47:59 -0500 |
|---|---|---|
| committer | brian cully <bjc@spork.org> | 2025-12-23 21:47:59 -0500 |
| commit | fdd715f4e610ae68dcb5310c37cebfbf383b1a4c (patch) | |
| tree | ced540d95376ba015703eff141c1219fa66c8711 /site/arena.mjs | |
| parent | d1bc29694219a3d3d34e6dcc696ce249c7540fcd (diff) | |
| download | automathon-fdd715f4e610ae68dcb5310c37cebfbf383b1a4c.tar.gz automathon-fdd715f4e610ae68dcb5310c37cebfbf383b1a4c.zip | |
js: move arena stuff to module
Diffstat (limited to 'site/arena.mjs')
| -rw-r--r-- | site/arena.mjs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/site/arena.mjs b/site/arena.mjs new file mode 100644 index 0000000..c76f7dc --- /dev/null +++ b/site/arena.mjs @@ -0,0 +1,79 @@ +const CANVAS_SELECTOR = 'canvas'; +const FPS_SELECTOR = '#fps'; + +export default class extends HTMLElement { + static name = 'x-arena'; + static register() { + console.debug('registering custom element', this.name, this); + self.customElements.define(this.name, this); + } + static radius = 25; + + #canvas; + #ctx; + #lastTime; + tickMS; + + connectedCallback() { + console.debug('connectedCallback', this); + this.#canvas = this.querySelector(CANVAS_SELECTOR); + this.#ctx = this.#canvas.getContext('2d'); + } + + invalidateFPS() { + document.querySelector(FPS_SELECTOR).textContent = 'n/a'; + } + + clamp(radius, x, y) { + const xx = Math.min(Math.max(radius, x), this.#canvas.width-radius); + const yy = Math.min(Math.max(radius, y), this.#canvas.height-radius); + return [xx, yy]; + } + + randStart(robo) { + const [x, y] = [this.#canvas.width, this.#canvas.height].map(len => + this.constructor.radius + Math.floor(Math.random() * (len - 2*this.constructor.radius))); + return {...robo, x, y}; + } + + renderFPS(now) { + const delta = now - (this.#lastTime ?? now); + if (delta > 0) { + document.querySelector(FPS_SELECTOR).textContent = Math.floor(1_000 / delta); + } + } + + render(robos, now=0) { + // we often get called with the same time stamp many times in + // a row due to fingerprint-reduction tech. + if (now === this.#lastTime) { + return; + } + this.renderFPS(now); + this.#lastTime = now; + + this.#ctx.clearRect(0, 0, this.#canvas.width, this.#canvas.height); + + robos.forEach(robo => { + // interpolation factor for smoother movement independent + // of tick rate, but never tween more than 1 tick. + const delta = robo.lastTick ? now - robo.lastTick : 0; + const timeScale = this.tickMS > 0 ? Math.min(delta / this.tickMS, 1.0) : 1; + let [x, y] = [robo.x, robo.y]; + if (delta > 0) { + const [velx, vely] = [ + robo.speedx, robo.speedy + ].map(x => timeScale * x); + [x, y] = this.clamp(this.constructor.radius, robo.x + velx, robo.y + vely); + } + this.#renderRobo(x, y); + }); + } + + #renderRobo(x, y) { + this.#ctx.fillStyle = 'rgb(200 0 0)'; + this.#ctx.beginPath(); + this.#ctx.arc(x, y, this.constructor.radius, 0, 2 * Math.PI); + this.#ctx.fill(); + } +} |
