feat: add image viewer, carousel, and pan/zoom features
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
export const THROW_MIN_DISTANCE = 14;
|
||||
export const THROW_GRACE_MS = 24;
|
||||
export const THROW_STALE_MS = 90;
|
||||
|
||||
export function freshness(idleMs: number): number {
|
||||
if (idleMs <= THROW_GRACE_MS) return 1;
|
||||
return Math.max(0, 1 - (idleMs - THROW_GRACE_MS) / (THROW_STALE_MS - THROW_GRACE_MS));
|
||||
}
|
||||
|
||||
export type Vec = { x: number; y: number };
|
||||
|
||||
export function createVelocityTracker() {
|
||||
let last: { x: number; y: number; t: number } | null = null;
|
||||
let vx = 0;
|
||||
let vy = 0;
|
||||
|
||||
return {
|
||||
reset(x: number, y: number) {
|
||||
last = { x, y, t: performance.now() };
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
},
|
||||
sample(x: number, y: number) {
|
||||
const now = performance.now();
|
||||
if (last) {
|
||||
const dt = now - last.t;
|
||||
if (dt > 0) {
|
||||
vx = vx * 0.7 + ((x - last.x) / dt) * 0.3;
|
||||
vy = vy * 0.7 + ((y - last.y) / dt) * 0.3;
|
||||
}
|
||||
}
|
||||
last = { x, y, t: now };
|
||||
},
|
||||
release(): Vec {
|
||||
const idle = last ? performance.now() - last.t : Infinity;
|
||||
const f = freshness(idle);
|
||||
return { x: vx * f, y: vy * f };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function runInertia(
|
||||
velocity: Vec,
|
||||
onStep: (dx: number, dy: number) => void,
|
||||
opts: { minSpeed?: number; stopSpeed?: number; decayPerFrame?: number } = {},
|
||||
): () => void {
|
||||
const minSpeed = opts.minSpeed ?? 0.04;
|
||||
const stopSpeed = opts.stopSpeed ?? 0.012;
|
||||
const decayPerFrame = opts.decayPerFrame ?? 0.94;
|
||||
let { x: vx, y: vy } = velocity;
|
||||
if (Math.hypot(vx, vy) < minSpeed) return () => { /* nothing started */ };
|
||||
|
||||
let raf = 0;
|
||||
let last = performance.now();
|
||||
const tick = (now: number) => {
|
||||
const dt = Math.min(40, now - last);
|
||||
last = now;
|
||||
onStep(vx * dt, vy * dt);
|
||||
const decay = Math.pow(decayPerFrame, dt / 16);
|
||||
vx *= decay;
|
||||
vy *= decay;
|
||||
if (Math.hypot(vx, vy) > stopSpeed) {
|
||||
raf = requestAnimationFrame(tick);
|
||||
} else {
|
||||
raf = 0;
|
||||
}
|
||||
};
|
||||
raf = requestAnimationFrame(tick);
|
||||
return () => { if (raf) cancelAnimationFrame(raf); };
|
||||
}
|
||||
Reference in New Issue
Block a user