UNPKG

@humanspeak/svelte-motion

Version:

Framer Motion for Svelte 5. Declarative motion.<tag> components with AnimatePresence exit animations, gestures (hover, tap, drag, focus, in-view), variants, FLIP layout animations, shared-layout transitions, spring physics, and scroll-linked motion values

52 lines (51 loc) 2.22 kB
import { attachFollow, isMotionValue, motionValue } from 'motion-dom'; import {} from 'svelte/store'; import { augmentMotionValue, isSvelteReadable, sampleSource } from './augmentMotionValue.svelte.js'; export function useFollowValue(source, options = {}) { // SSR: return a static MotionValue with no animation. Reads return the // best-effort initial value; .set / .jump become no-ops to avoid drifting // away from the server-rendered snapshot. if (typeof window === 'undefined') { const initial = sampleSource(source); const ssrValue = motionValue(initial); ssrValue.set = () => undefined; ssrValue.jump = () => undefined; return augmentMotionValue(ssrValue); } // Resolve initial + follow source. Svelte readables get bridged into a // motion-dom MotionValue so `attachFollow` can subscribe to their emits. let followSource; let cleanupReadableBridge; let svelteBridge; if (isMotionValue(source)) { followSource = source; } else if (isSvelteReadable(source)) { const initialFromReadable = sampleSource(source); svelteBridge = motionValue(initialFromReadable); cleanupReadableBridge = source.subscribe((v) => { // Svelte readable contract emits synchronously on subscribe. Skip // the initial emit (already seeded above) so attachFollow doesn't // fire an animation on attach. if (svelteBridge.get() === v) return; svelteBridge.set(v); }); followSource = svelteBridge; } else { followSource = source; } const initial = isMotionValue(followSource) ? followSource.get() : followSource; const value = motionValue(initial); // Default transition is `spring` — matches React framer-motion's // `useFollowValue` default. Caller's `type` (if set) overrides. const stopFollow = attachFollow(value, followSource, { type: 'spring', ...options }); const dispose = () => { stopFollow?.(); cleanupReadableBridge?.(); svelteBridge?.destroy(); }; $effect(() => () => value.destroy()); return augmentMotionValue(value, dispose); }