fuzzyworld

bubbles / files / src / input / TouchInput.ts

TouchInput.ts

Blame: the commit that last changed each line · file · history

dcc4757f 2026-04-09 Pavel Safronov1import { RigidBody } from '../physics/RigidBody.js';
2import { Vector2 } from '../physics/Vector2.js';
3import { World } from '../physics/World.js';
4import { ICONS } from '../icons/registry.js';
5
6/**
7 * Listens for pointer-down events on the canvas and spawns a random icon
8 * body near the water surface at the tap position.
9 */
10export class TouchInput {
11 constructor(
12 private readonly canvas: HTMLCanvasElement,
13 private readonly world: World,
14 private readonly onRequestPermission: () => Promise<void>,
15 ) {
16 this.canvas.addEventListener('pointerdown', this.handlePointer);
17 }
18
19 private readonly handlePointer = async (e: PointerEvent): Promise<void> => {
20 e.preventDefault();
21 // Request iOS orientation permission on very first tap
22 await this.onRequestPermission();
23 this.spawnAt(e.clientX, e.clientY);
24 };
25
26 private spawnAt(clientX: number, clientY: number): void {
27 const rect = this.canvas.getBoundingClientRect();
fc6e1fbf 2026-04-09 Pavel Safronov28 // World coordinates are CSS pixels (the renderer scales by DPR via ctx.scale),
29 // so we just subtract the canvas offset — no DPR multiplication needed.
30 const canvasX = clientX - rect.left;
31 const canvasY = clientY - rect.top;
dcc4757f 2026-04-09 Pavel Safronov32
33 // Clamp to glass interior
34 const gx = this.world.glassX;
35 const gy = this.world.glassY;
36 const gw = this.world.glassW;
37
38 const icon = ICONS[Math.floor(Math.random() * ICONS.length)]!;
39 const r = icon.radius;
40
fc6e1fbf 2026-04-09 Pavel Safronov41 // Clamp spawn position to glass interior
dcc4757f 2026-04-09 Pavel Safronov42 const spawnX = Math.max(gx + r, Math.min(gx + gw - r, canvasX));
fc6e1fbf 2026-04-09 Pavel Safronov43 const spawnY = Math.max(gy + r, Math.min(gy + this.world.glassH - r, canvasY));
dcc4757f 2026-04-09 Pavel Safronov44
45 const body = new RigidBody({
46 position: new Vector2(spawnX, spawnY),
47 radius: r,
48 density: icon.density,
49 label: icon.label,
50 });
51
fc6e1fbf 2026-04-09 Pavel Safronov52 body.velocity.x = (Math.random() - 0.5) * 80;
53 body.velocity.y = 0;
dcc4757f 2026-04-09 Pavel Safronov54 body.angularVelocity = (Math.random() - 0.5) * 4;
55
56 this.world.addBody(body);
57 }
58
59 destroy(): void {
60 this.canvas.removeEventListener('pointerdown', this.handlePointer);
61 }
62}