feat(stammbaum): animate fit-to-screen, snap under reduced motion (#692)
Fit-to-screen tweens to the default view over 300ms via animateView (eased, lerpView-driven) and snaps instantly when prefers-reduced-motion is set (US-PAN-004 AC2, NFR-A11Y-003). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
38
frontend/src/lib/person/genealogy/animateView.ts
Normal file
38
frontend/src/lib/person/genealogy/animateView.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { lerpView, type PanZoomState } from '$lib/person/genealogy/panZoom';
|
||||
|
||||
/** Fit / recentre animation duration (US-PAN-004 AC2: ≤ 300 ms). */
|
||||
export const VIEW_ANIM_MS = 300;
|
||||
|
||||
const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3);
|
||||
|
||||
/** Snapshot of the user's reduced-motion preference (non-reactive, browser-only). */
|
||||
export function prefersReducedMotion(): boolean {
|
||||
return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
||||
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tween the view from `from` to `to`, calling `onFrame` with each interpolated
|
||||
* state. Honors reduced motion by snapping straight to the target (NFR-A11Y-003,
|
||||
* REQ-PAN-005). Returns a cancel function.
|
||||
*/
|
||||
export function animateView(
|
||||
from: PanZoomState,
|
||||
to: PanZoomState,
|
||||
onFrame: (state: PanZoomState) => void,
|
||||
opts: { reducedMotion?: boolean; durationMs?: number } = {}
|
||||
): () => void {
|
||||
if (opts.reducedMotion) {
|
||||
onFrame(to);
|
||||
return () => {};
|
||||
}
|
||||
const duration = opts.durationMs ?? VIEW_ANIM_MS;
|
||||
const start = performance.now();
|
||||
let raf = requestAnimationFrame(function step(now: number) {
|
||||
const t = Math.min(1, (now - start) / duration);
|
||||
onFrame(lerpView(from, to, easeOutCubic(t)));
|
||||
if (t < 1) raf = requestAnimationFrame(step);
|
||||
});
|
||||
return () => cancelAnimationFrame(raf);
|
||||
}
|
||||
Reference in New Issue
Block a user