mirror of
https://github.com/andwati/andwati.com.git
synced 2026-10-18 01:12:53 +00:00
Add a 404 page concept and a spring-physics motion primitive
404: "Page Fault" — a SIGSEGV/memory-fault framing distinct from the retired terminal easter egg, still fitting the CS-flavored identity. Plain/unstyled for now like the rest of the placeholder layout; restyle once the design system lands. src/lib/spring.ts: a small CSS+Web Animations API spring utility (damped harmonic oscillator -> keyframe progress values), per the locked "no animation library" decision. Not wired into any component yet — this is the foundational primitive the design system's motion work will build on, kept free of any visual/component opinions.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
export interface SpringOptions {
|
||||
stiffness?: number;
|
||||
damping?: number;
|
||||
mass?: number;
|
||||
/** Stop simulating once position and velocity are both within this of rest. */
|
||||
precision?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a damped harmonic oscillator from 0 to 1 and returns per-frame
|
||||
* progress values (60fps), for use as Web Animations API keyframes. Pure
|
||||
* physics simulation, no DOM/CSS opinions — see `animateSpring` below for
|
||||
* the usual way to consume it.
|
||||
*/
|
||||
export function springProgress({
|
||||
stiffness = 170,
|
||||
damping = 26,
|
||||
mass = 1,
|
||||
precision = 0.005,
|
||||
}: SpringOptions = {}): number[] {
|
||||
const frames: number[] = [];
|
||||
let position = 0;
|
||||
let velocity = 0;
|
||||
const dt = 1 / 60;
|
||||
const maxDuration = 4; // seconds, safety cap against under-damped configs
|
||||
for (let t = 0; t < maxDuration; t += dt) {
|
||||
const acceleration =
|
||||
(-stiffness * (position - 1) - damping * velocity) / mass;
|
||||
velocity += acceleration * dt;
|
||||
position += velocity * dt;
|
||||
frames.push(position);
|
||||
if (Math.abs(1 - position) < precision && Math.abs(velocity) < precision)
|
||||
break;
|
||||
}
|
||||
frames.push(1);
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates `element` from `from` to `to` along a spring curve, via the
|
||||
* native Web Animations API — no animation library dependency. `toKeyframe`
|
||||
* shapes each interpolated value into whatever CSS properties you need,
|
||||
* e.g. `(v) => ({ transform: \`translateY(${(1 - v) * 20}px)\`, opacity: v })`.
|
||||
* Honors `prefers-reduced-motion` by jumping straight to the end state.
|
||||
*/
|
||||
export function animateSpring(
|
||||
element: HTMLElement,
|
||||
options: SpringOptions & {
|
||||
from: number;
|
||||
to: number;
|
||||
toKeyframe: (value: number) => Keyframe;
|
||||
},
|
||||
): Animation {
|
||||
const { from, to, toKeyframe, ...spring } = options;
|
||||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
||||
return element.animate([toKeyframe(to)], { duration: 1, fill: "forwards" });
|
||||
}
|
||||
const progress = springProgress(spring);
|
||||
const keyframes = progress.map((p) => toKeyframe(from + (to - from) * p));
|
||||
const durationMs = (progress.length / 60) * 1000;
|
||||
return element.animate(keyframes, {
|
||||
duration: durationMs,
|
||||
fill: "forwards",
|
||||
easing: "linear",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
import Base from "../layouts/Base.astro";
|
||||
---
|
||||
|
||||
<Base title="Page Fault" description="404 — this page is not mapped in this process's address space.">
|
||||
<h1>Page Fault</h1>
|
||||
<p class="meta">SIGSEGV — the address you requested was never mapped.</p>
|
||||
<p>
|
||||
Whatever used to live here has been moved, renamed, or never existed in
|
||||
this address space to begin with. Unlike a real page fault, there's no
|
||||
handler that's going to page this back in for you.
|
||||
</p>
|
||||
<p><a href="/">Return to a valid address →</a></p>
|
||||
</Base>
|
||||
Reference in New Issue
Block a user