beter crossfade
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import type { TransitionConfig } from 'svelte/transition';
|
||||
|
||||
const prefersReducedMotion = () =>
|
||||
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
/**
|
||||
* Fades a loading placeholder out *over* the content that replaces it.
|
||||
*
|
||||
* A plain `out:fade` is not usable on a skeleton: Svelte keeps the node in the
|
||||
* layout for the length of the transition, so the real content stacks below the
|
||||
* placeholder and the whole page jumps up the moment the placeholder finally
|
||||
* unmounts. That is why these placeholders only ever faded in - the swap was
|
||||
* abrupt, but at least nothing moved.
|
||||
*
|
||||
* This pins the placeholder to the box it already occupies and takes it out of
|
||||
* flow for the fade instead. The content settles into its final position
|
||||
* immediately and the skeleton dissolves on top of it.
|
||||
*
|
||||
* Requirements at the call site: the placeholder's parent must be positioned
|
||||
* (`class="relative"`) and must be the same element that renders the real
|
||||
* content, or the overlay lands somewhere else on the page.
|
||||
*/
|
||||
export function skeletonOut(node: HTMLElement, { duration = 260 } = {}): TransitionConfig {
|
||||
if (prefersReducedMotion()) return { duration: 0 };
|
||||
|
||||
// measured while the node is still in flow, which is when Svelte builds the
|
||||
// transition - the values below are what freeze it in place
|
||||
const { offsetTop, offsetLeft, offsetWidth, offsetHeight } = node;
|
||||
|
||||
return {
|
||||
duration,
|
||||
css: (t) => `
|
||||
opacity: ${t};
|
||||
position: absolute;
|
||||
top: ${offsetTop}px;
|
||||
left: ${offsetLeft}px;
|
||||
width: ${offsetWidth}px;
|
||||
height: ${offsetHeight}px;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
`
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user