This repository has been archived on 2026-08-10. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
drizzli/src/lib/utils/day-swap.ts
T
2026-08-01 15:27:26 +02:00

63 lines
1.9 KiB
TypeScript

import { tick } from 'svelte';
const prefersReducedMotion = () =>
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
/**
* Runs a day change inside a view transition, so the outgoing day is still on
* screen while the incoming one fades in - a real cross-fade rather than the
* old content vanishing and the new one fading up from nothing.
*
* Only the regions that actually change carry a `view-transition-name` (see
* `.day-region-*` in routes/layout.css); everything else - the strip, the
* header, the page chrome - is pinned by the `day-switch` class so it stays
* completely still.
*/
export async function runDayTransition(update: () => void): Promise<void> {
if (prefersReducedMotion() || !document.startViewTransition) {
update();
return;
}
const root = document.documentElement;
root.classList.add('day-switch');
try {
// Svelte applies the change on the next tick; the transition has to wait
// for that before it snapshots the new state.
await document.startViewTransition(async () => {
update();
await tick();
}).finished;
} catch {
/* a superseded transition is fine - the DOM is already up to date */
} finally {
root.classList.remove('day-switch');
}
}
/**
* Fallback for browsers without view transitions: fade the block back in when
* the value passed to it changes. Animates the existing node rather than
* remounting it, so the canvas charts keep their zoom state.
*/
export function daySwap(node: HTMLElement, key: unknown) {
let current = key;
const play = () => {
// view transitions handle it properly where they exist
if (typeof document.startViewTransition === 'function' || prefersReducedMotion()) return;
node.animate([{ opacity: 0.1 }, { opacity: 1 }], {
duration: 460,
easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)'
});
};
return {
update(next: unknown) {
if (next === current) return;
current = next;
play();
}
};
}