Papure/src/lib/ink/fling.ts
agent 5a501a5c87 Offer a new page after a finger fling past the page edge
A quick one-finger flick toward a free side of the active page, once
that edge is on screen, highlights and pulses the plus button on that
side for three seconds. It never adds a page by itself, so ordinary
panning can't create pages; a tap on the highlighted plus does.

Fling detection lives in ink/fling.ts with tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GaoZtJQZwLXkEgWs8twCia
2026-09-27 21:29:16 +00:00

41 lines
1.3 KiB
TypeScript

// Fling detection for one-finger pans: a quick flick that ends the drag.
import type { Dir } from '$lib/model/tree';
export interface Sample {
x: number;
y: number;
t: number;
}
/** Only the last stretch of the drag counts. */
const WINDOW_MS = 100;
/** px/ms; a slow drag that stops is not a fling. */
const MIN_SPEED = 0.6;
/** The main axis must dominate by this much. */
const MIN_RATIO = 2;
/**
* Side of the canvas a fling heads for, or null. Moving the finger left pulls in
* what's on the right, so a leftward flick returns 'right'.
*/
export function flingDir(samples: readonly Sample[]): Dir | null {
const last = samples.at(-1);
if (!last) return null;
const first = samples.find((s) => last.t - s.t <= WINDOW_MS);
if (!first || first === last) return null;
const dt = last.t - first.t;
if (dt <= 0) return null;
const dx = last.x - first.x;
const dy = last.y - first.y;
if (Math.hypot(dx, dy) / dt < MIN_SPEED) return null;
if (Math.abs(dx) >= Math.abs(dy) * MIN_RATIO) return dx < 0 ? 'right' : 'left';
if (Math.abs(dy) >= Math.abs(dx) * MIN_RATIO) return dy < 0 ? 'down' : 'up';
return null;
}
/** Keeps a short trail of pointer samples for flingDir. */
export function pushSample(samples: Sample[], s: Sample) {
samples.push(s);
while (samples.length > 2 && s.t - samples[0].t > WINDOW_MS * 2) samples.shift();
}