diff --git a/package.json b/package.json index 98e0d2c..b30fa87 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "scripts": { "build": "vite build", "dev": "vite build --watch", + "dev:demo": "bun run --cwd ./demo dev", "test": "vitest", "test:run": "vitest run", "test:coverage": "vitest run --coverage", diff --git a/src/core/simulation.ts b/src/core/simulation.ts index fb08417..9f095b0 100644 --- a/src/core/simulation.ts +++ b/src/core/simulation.ts @@ -131,6 +131,7 @@ export class FluidSimulation { #config: FluidConfig; #mouse: MouseState = { x: 0, y: 0, dx: 0, dy: 0, targetX: 0, targetY: 0, moved: false }; + #mouseSeeded = false; #source: Source | null = null; @@ -219,6 +220,13 @@ export class FluidSimulation { } handleMove(x: number, y: number, strength = 1): void { + if (!this.#mouseSeeded) { + // Seed position on first call so the initial delta isn't measured from (0,0). + this.#mouse.x = this.#mouse.targetX = x; + this.#mouse.y = this.#mouse.targetY = y; + this.#mouseSeeded = true; + return; + } this.#mouse.moved = true; this.#mouse.dx = (x - this.#mouse.targetX) * strength; this.#mouse.dy = (y - this.#mouse.targetY) * strength; diff --git a/src/fluid-controller.ts b/src/fluid-controller.ts index 1e83512..a3a4026 100644 --- a/src/fluid-controller.ts +++ b/src/fluid-controller.ts @@ -134,7 +134,7 @@ export class FluidController { if (this.#worker) { this.#worker.postMessage({ type: 'resize', width, height, dpr: effectiveDpr }); } else { - this.#sim!.resize(width, height, effectiveDpr); + this.#sim?.resize(width, height, effectiveDpr); } } diff --git a/src/react/useFluid.ts b/src/react/useFluid.ts index fdef1b2..11f2e9b 100644 --- a/src/react/useFluid.ts +++ b/src/react/useFluid.ts @@ -80,6 +80,14 @@ export function useFluid( }); controllerRef.current = controller; + // Immediately sync dimensions so #dpr is correct from the first frame. + // The ResizeObserver only fires when the container *size* changes, so it + // won't re-fire on an alphaEnabled/webGPUEnabled toggle where the container + // hasn't moved. Without this call the simulation keeps #dpr=1 and all + // pointer-to-UV coordinate transforms are wrong until something else + // triggers a resize. + if (initW > 0 && initH > 0) controller.resize(initW, initH); + // Forward container resizes to the simulation — reads clampedDprRef so pixelRatio changes are picked up const ro = new ResizeObserver((entries) => { for (const entry of entries) {