@humanspeak/svelte-motion 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type { AugmentedMotionValue } from './utils/augmentMotionValue.svelte';
|
|
|
14
14
|
export { useCycle } from './utils/cycle.svelte';
|
|
15
15
|
export type { Cycle, CycleState } from './utils/cycle.svelte';
|
|
16
16
|
export { createDragControls } from './utils/dragControls';
|
|
17
|
+
export { useFollowValue } from './utils/followValue.svelte';
|
|
18
|
+
export type { FollowMotionValue, UseFollowValueOptions } from './utils/followValue.svelte';
|
|
17
19
|
export { useInView } from './utils/inView.svelte';
|
|
18
20
|
export type { InViewState, UseInViewOptions } from './utils/inView.svelte';
|
|
19
21
|
export { useMotionTemplate } from './utils/motionTemplate.svelte';
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export { useAnimate } from './utils/animate.svelte';
|
|
|
13
13
|
export { useAnimationFrame } from './utils/animationFrame';
|
|
14
14
|
export { useCycle } from './utils/cycle.svelte';
|
|
15
15
|
export { createDragControls } from './utils/dragControls';
|
|
16
|
+
export { useFollowValue } from './utils/followValue.svelte';
|
|
16
17
|
export { useInView } from './utils/inView.svelte';
|
|
17
18
|
export { useMotionTemplate } from './utils/motionTemplate.svelte';
|
|
18
19
|
export { useMotionValue } from './utils/motionValue.svelte';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type FollowValueOptions, type MotionValue } from 'motion-dom';
|
|
2
|
+
import { type Readable } from 'svelte/store';
|
|
3
|
+
import { type AugmentedMotionValue } from './augmentMotionValue.svelte.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options accepted by {@link useFollowValue}. Mirrors framer-motion's
|
|
6
|
+
* `FollowValueOptions` 1:1 — any `ValueAnimationTransition` shape (spring /
|
|
7
|
+
* tween / inertia / keyframes) plus `skipInitialAnimation` for
|
|
8
|
+
* scroll-restoration scenarios.
|
|
9
|
+
*
|
|
10
|
+
* The shape is re-exported from motion-dom so consumers don't have to
|
|
11
|
+
* cross-import.
|
|
12
|
+
*
|
|
13
|
+
* @see https://motion.dev/docs/react-use-follow-value
|
|
14
|
+
*/
|
|
15
|
+
export type UseFollowValueOptions = FollowValueOptions;
|
|
16
|
+
/**
|
|
17
|
+
* The augmented `MotionValue` returned by {@link useFollowValue} (and, since
|
|
18
|
+
* `useSpring` now delegates here, by `useSpring` too).
|
|
19
|
+
*/
|
|
20
|
+
export type FollowMotionValue<T extends number | string> = AugmentedMotionValue<T>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates an animated `MotionValue` that, when `.set(v)` is called, animates
|
|
23
|
+
* toward `v` using **any** transition type — spring, tween, inertia, or
|
|
24
|
+
* keyframes. Pass another `MotionValue` (or Svelte readable) as the source
|
|
25
|
+
* and the result follows it: every source emit kicks off a new animation
|
|
26
|
+
* toward the latest value.
|
|
27
|
+
*
|
|
28
|
+
* Mirrors React framer-motion's `useFollowValue` 1:1. `useSpring` in this
|
|
29
|
+
* library is now a thin wrapper that delegates here with the default
|
|
30
|
+
* `{ type: 'spring' }`.
|
|
31
|
+
*
|
|
32
|
+
* Returned object is a real motion-dom `MotionValue` (composes with
|
|
33
|
+
* `useTransform`, `useVelocity`, `animate()`, etc.) augmented with a
|
|
34
|
+
* `$state`-backed `.current` getter and a Svelte readable `.subscribe`
|
|
35
|
+
* shim.
|
|
36
|
+
*
|
|
37
|
+
* Lifecycle: must be called during component initialization. Cleanup is
|
|
38
|
+
* registered via `$effect`; the follow animation stops, the source bridge
|
|
39
|
+
* (if any) tears down, and `value.destroy()` runs when the surrounding
|
|
40
|
+
* `$effect` scope unmounts.
|
|
41
|
+
*
|
|
42
|
+
* SSR-safe: returns a static augmented `MotionValue` with no animation on
|
|
43
|
+
* the server. `.set` and `.jump` become no-ops to avoid drifting away from
|
|
44
|
+
* the server-rendered snapshot.
|
|
45
|
+
*
|
|
46
|
+
* @template T The value type — `number` or `string` (unit strings preserved).
|
|
47
|
+
* @param source Initial value, a `MotionValue` to follow, or a Svelte readable.
|
|
48
|
+
* @param options Transition + follow configuration (`type: 'spring' | 'tween' | 'inertia' | 'keyframes'`, plus the corresponding per-type options).
|
|
49
|
+
* @returns A `MotionValue<T>` with `.current` and `.subscribe`.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```svelte
|
|
53
|
+
* <script lang="ts">
|
|
54
|
+
* import { useFollowValue, useMotionValue } from '@humanspeak/svelte-motion'
|
|
55
|
+
*
|
|
56
|
+
* const target = useMotionValue(0)
|
|
57
|
+
*
|
|
58
|
+
* // Spring follow (default — same as `useSpring(target)`).
|
|
59
|
+
* const spring = useFollowValue(target, { stiffness: 300, damping: 30 })
|
|
60
|
+
*
|
|
61
|
+
* // Tween follow — eased linear interpolation.
|
|
62
|
+
* const eased = useFollowValue(target, {
|
|
63
|
+
* type: 'tween',
|
|
64
|
+
* duration: 0.4,
|
|
65
|
+
* ease: 'easeInOut'
|
|
66
|
+
* })
|
|
67
|
+
*
|
|
68
|
+
* // Inertia — decays from initial velocity.
|
|
69
|
+
* const drifting = useFollowValue(0, { type: 'inertia', velocity: 800, power: 0.6 })
|
|
70
|
+
* </script>
|
|
71
|
+
*
|
|
72
|
+
* <button onclick={() => target.set(target.get() === 0 ? 200 : 0)}>Toggle</button>
|
|
73
|
+
* <div style="transform: translateX({spring.current}px)">spring</div>
|
|
74
|
+
* <div style="transform: translateX({eased.current}px)">tween</div>
|
|
75
|
+
* <div style="transform: translateX({drifting.current}px)">inertia</div>
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @see https://motion.dev/docs/react-use-follow-value
|
|
79
|
+
*/
|
|
80
|
+
export declare function useFollowValue(source: number, options?: UseFollowValueOptions): FollowMotionValue<number>;
|
|
81
|
+
export declare function useFollowValue(source: string, options?: UseFollowValueOptions): FollowMotionValue<string>;
|
|
82
|
+
export declare function useFollowValue(source: MotionValue<number>, options?: UseFollowValueOptions): FollowMotionValue<number>;
|
|
83
|
+
export declare function useFollowValue(source: MotionValue<string>, options?: UseFollowValueOptions): FollowMotionValue<string>;
|
|
84
|
+
export declare function useFollowValue<T extends number | string>(source: Readable<T>, options?: UseFollowValueOptions): FollowMotionValue<T>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { attachFollow, isMotionValue, motionValue } from 'motion-dom';
|
|
2
|
+
import {} from 'svelte/store';
|
|
3
|
+
import { augmentMotionValue, isSvelteReadable, sampleSource } from './augmentMotionValue.svelte.js';
|
|
4
|
+
export function useFollowValue(source, options = {}) {
|
|
5
|
+
// SSR: return a static MotionValue with no animation. Reads return the
|
|
6
|
+
// best-effort initial value; .set / .jump become no-ops to avoid drifting
|
|
7
|
+
// away from the server-rendered snapshot.
|
|
8
|
+
if (typeof window === 'undefined') {
|
|
9
|
+
const initial = sampleSource(source);
|
|
10
|
+
const ssrValue = motionValue(initial);
|
|
11
|
+
ssrValue.set = () => undefined;
|
|
12
|
+
ssrValue.jump = () => undefined;
|
|
13
|
+
return augmentMotionValue(ssrValue);
|
|
14
|
+
}
|
|
15
|
+
// Resolve initial + follow source. Svelte readables get bridged into a
|
|
16
|
+
// motion-dom MotionValue so `attachFollow` can subscribe to their emits.
|
|
17
|
+
let followSource;
|
|
18
|
+
let cleanupReadableBridge;
|
|
19
|
+
let svelteBridge;
|
|
20
|
+
if (isMotionValue(source)) {
|
|
21
|
+
followSource = source;
|
|
22
|
+
}
|
|
23
|
+
else if (isSvelteReadable(source)) {
|
|
24
|
+
const initialFromReadable = sampleSource(source);
|
|
25
|
+
svelteBridge = motionValue(initialFromReadable);
|
|
26
|
+
cleanupReadableBridge = source.subscribe((v) => {
|
|
27
|
+
// Svelte readable contract emits synchronously on subscribe. Skip
|
|
28
|
+
// the initial emit (already seeded above) so attachFollow doesn't
|
|
29
|
+
// fire an animation on attach.
|
|
30
|
+
if (svelteBridge.get() === v)
|
|
31
|
+
return;
|
|
32
|
+
svelteBridge.set(v);
|
|
33
|
+
});
|
|
34
|
+
followSource = svelteBridge;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
followSource = source;
|
|
38
|
+
}
|
|
39
|
+
const initial = isMotionValue(followSource) ? followSource.get() : followSource;
|
|
40
|
+
const value = motionValue(initial);
|
|
41
|
+
// Default transition is `spring` — matches React framer-motion's
|
|
42
|
+
// `useFollowValue` default. Caller's `type` (if set) overrides.
|
|
43
|
+
const stopFollow = attachFollow(value, followSource, { type: 'spring', ...options });
|
|
44
|
+
const dispose = () => {
|
|
45
|
+
stopFollow?.();
|
|
46
|
+
cleanupReadableBridge?.();
|
|
47
|
+
svelteBridge?.destroy();
|
|
48
|
+
};
|
|
49
|
+
$effect(() => () => value.destroy());
|
|
50
|
+
return augmentMotionValue(value, dispose);
|
|
51
|
+
}
|
|
@@ -9,6 +9,10 @@ import { type AugmentedMotionValue } from './augmentMotionValue.svelte.js';
|
|
|
9
9
|
* `velocity`, `restDelta`, `restSpeed`) plus `skipInitialAnimation` for
|
|
10
10
|
* scroll-restoration scenarios.
|
|
11
11
|
*
|
|
12
|
+
* `useSpring` is a thin wrapper over {@link useFollowValue} that hard-codes
|
|
13
|
+
* `type: 'spring'`. For other transition types (tween, inertia, keyframes)
|
|
14
|
+
* use `useFollowValue` directly.
|
|
15
|
+
*
|
|
12
16
|
* @see https://motion.dev/docs/react-use-spring
|
|
13
17
|
*/
|
|
14
18
|
export type UseSpringOptions = SpringOptions & Pick<FollowValueOptions, 'skipInitialAnimation'>;
|
|
@@ -22,41 +26,36 @@ export type UseSpringOptions = SpringOptions & Pick<FollowValueOptions, 'skipIni
|
|
|
22
26
|
* - `current` — a Svelte-5 reactive read backed by `$state`. Use in templates
|
|
23
27
|
* and `$derived` / `$effect` to track the spring value without subscribing.
|
|
24
28
|
* - `subscribe` — Svelte readable store contract. Calls the run function once
|
|
25
|
-
* synchronously with the current value, then on every change.
|
|
26
|
-
* spring be used with `$spring` template syntax, `get(spring)`, and as a
|
|
27
|
-
* dependency in `useTransform`'s function form.
|
|
29
|
+
* synchronously with the current value, then on every change.
|
|
28
30
|
*/
|
|
29
31
|
export type SpringMotionValue<T extends number | string> = AugmentedMotionValue<T>;
|
|
30
32
|
/**
|
|
31
33
|
* Creates a spring-animated `MotionValue`.
|
|
32
34
|
*
|
|
33
35
|
* Set a target with `.set(v)` to animate to it using spring physics, or
|
|
34
|
-
* `.jump(v)` to skip the animation. Pass another `MotionValue` (or
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* whatever that source emits.
|
|
38
|
-
*
|
|
39
|
-
* Returned object is a real motion-dom `MotionValue` — composes with
|
|
40
|
-
* `animate()`, `useTransform`, `useVelocity`, and motion-dom's animation
|
|
41
|
-
* engine. On top, it exposes:
|
|
36
|
+
* `.jump(v)` to skip the animation. Pass another `MotionValue` (or a Svelte
|
|
37
|
+
* readable store from `useScroll` / `useTime`) as `source` and the spring
|
|
38
|
+
* will animate toward whatever that source emits.
|
|
42
39
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* Tier 2 migration window.
|
|
40
|
+
* Returned object is a real motion-dom `MotionValue` augmented with a
|
|
41
|
+
* `$state`-backed `.current` getter and a Svelte readable `.subscribe` shim.
|
|
42
|
+
* Composes with `useTransform`, `useVelocity`, `animate()`, and the rest of
|
|
43
|
+
* the motion-value surface.
|
|
48
44
|
*
|
|
49
|
-
* Lifecycle: must be called during component initialization.
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* `.destroy()` to clean up early.
|
|
45
|
+
* Lifecycle: must be called during component initialization. The follow
|
|
46
|
+
* animation, source bridge (if any), and motion value teardown are bound to
|
|
47
|
+
* the surrounding `$effect` scope.
|
|
53
48
|
*
|
|
54
49
|
* SSR-safe: returns a static `MotionValue` with no animation on the server.
|
|
55
50
|
*
|
|
56
|
-
* @
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
51
|
+
* Implementation: thin wrapper over {@link useFollowValue} that hard-codes
|
|
52
|
+
* `type: 'spring'`. For tween / inertia / keyframes follows, call
|
|
53
|
+
* `useFollowValue` directly.
|
|
54
|
+
*
|
|
55
|
+
* @template T The value type — `number` or `string` (unit strings preserved).
|
|
56
|
+
* @param source Initial value, a `MotionValue` to follow, or a Svelte readable.
|
|
57
|
+
* @param options Spring + follow configuration.
|
|
58
|
+
* @returns A `MotionValue<T>` with `.current` and `.subscribe`.
|
|
60
59
|
*
|
|
61
60
|
* @example
|
|
62
61
|
* ```svelte
|
|
@@ -67,7 +66,7 @@ export type SpringMotionValue<T extends number | string> = AugmentedMotionValue<
|
|
|
67
66
|
* </script>
|
|
68
67
|
*
|
|
69
68
|
* <button onclick={() => x.set(100)}>Animate</button>
|
|
70
|
-
* <div>{x.current}</div>
|
|
69
|
+
* <div style="transform: translateX({x.current}px)">{x.current}</div>
|
|
71
70
|
* ```
|
|
72
71
|
*
|
|
73
72
|
* @see https://motion.dev/docs/react-use-spring
|
|
@@ -1,54 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {} from 'motion-dom';
|
|
2
2
|
import {} from 'svelte/store';
|
|
3
|
-
import {
|
|
3
|
+
import {} from './augmentMotionValue.svelte.js';
|
|
4
|
+
import { useFollowValue } from './followValue.svelte.js';
|
|
4
5
|
export function useSpring(source, options = {}) {
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
const initial = sampleSource(source);
|
|
10
|
-
const ssrValue = motionValue(initial);
|
|
11
|
-
ssrValue.set = () => undefined;
|
|
12
|
-
ssrValue.jump = () => undefined;
|
|
13
|
-
return augmentMotionValue(ssrValue);
|
|
14
|
-
}
|
|
15
|
-
// Resolve initial + follow source.
|
|
16
|
-
let followSource;
|
|
17
|
-
let cleanupReadableBridge;
|
|
18
|
-
let svelteBridge;
|
|
19
|
-
if (isMotionValue(source)) {
|
|
20
|
-
followSource = source;
|
|
21
|
-
}
|
|
22
|
-
else if (isSvelteReadable(source)) {
|
|
23
|
-
// Bridge a Svelte readable into a MotionValue so attachFollow can
|
|
24
|
-
// track it. Synchronous initial sample comes from svelte/store's get().
|
|
25
|
-
const initialFromReadable = sampleSource(source);
|
|
26
|
-
svelteBridge = motionValue(initialFromReadable);
|
|
27
|
-
cleanupReadableBridge = source.subscribe((v) => {
|
|
28
|
-
// The Svelte readable contract calls the subscriber synchronously
|
|
29
|
-
// with the current value on subscribe. Skip if it equals the
|
|
30
|
-
// already-seeded bridge value so attachFollow doesn't fire a
|
|
31
|
-
// spring on the initial emit. Subsequent emits go through set()
|
|
32
|
-
// and trigger animation.
|
|
33
|
-
if (svelteBridge.get() === v)
|
|
34
|
-
return;
|
|
35
|
-
svelteBridge.set(v);
|
|
36
|
-
});
|
|
37
|
-
followSource = svelteBridge;
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
followSource = source;
|
|
41
|
-
}
|
|
42
|
-
const initial = isMotionValue(followSource) ? followSource.get() : followSource;
|
|
43
|
-
const value = motionValue(initial);
|
|
44
|
-
const stopFollow = attachFollow(value, followSource, { type: 'spring', ...options });
|
|
45
|
-
// Side-cleanup for our augmentations. Single-shot guard lives in the
|
|
46
|
-
// augmented `value.destroy` (the only caller), so no flag here.
|
|
47
|
-
const dispose = () => {
|
|
48
|
-
stopFollow?.();
|
|
49
|
-
cleanupReadableBridge?.();
|
|
50
|
-
svelteBridge?.destroy();
|
|
51
|
-
};
|
|
52
|
-
$effect(() => () => value.destroy());
|
|
53
|
-
return augmentMotionValue(value, dispose);
|
|
6
|
+
// Cast through `unknown` because TypeScript can't narrow the multi-form
|
|
7
|
+
// `source` against `useFollowValue`'s overload set; runtime behavior is
|
|
8
|
+
// identical — `useFollowValue` dispatches on the same shapes.
|
|
9
|
+
return useFollowValue(source, { type: 'spring', ...options });
|
|
54
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "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. The drop-in Framer Motion alternative for Svelte and SvelteKit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|