63 lines
1.9 KiB
TypeScript
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();
|
|
}
|
|
};
|
|
}
|