@humanspeak/svelte-motion 0.4.7 → 0.4.9
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/html/_MotionContainer.svelte +8 -8
- package/dist/index.d.ts +9 -7
- package/dist/index.js +5 -5
- package/dist/utils/booleanSnapshot.svelte.d.ts +37 -0
- package/dist/utils/booleanSnapshot.svelte.js +48 -0
- package/dist/utils/cycle.svelte.d.ts +98 -0
- package/dist/utils/cycle.svelte.js +44 -0
- package/dist/utils/inView.svelte.d.ts +209 -0
- package/dist/utils/inView.svelte.js +323 -0
- package/dist/utils/reducedMotion.svelte.d.ts +43 -0
- package/dist/utils/reducedMotion.svelte.js +80 -0
- package/dist/utils/reducedMotionConfig.svelte.d.ts +74 -0
- package/dist/utils/reducedMotionConfig.svelte.js +144 -0
- package/dist/utils/spring.svelte.d.ts +83 -0
- package/dist/utils/spring.svelte.js +118 -0
- package/package.json +8 -8
- package/dist/utils/cycle.d.ts +0 -35
- package/dist/utils/cycle.js +0 -48
- package/dist/utils/inView.d.ts +0 -136
- package/dist/utils/inView.js +0 -266
- package/dist/utils/reducedMotion.d.ts +0 -20
- package/dist/utils/reducedMotion.js +0 -42
- package/dist/utils/reducedMotionConfig.d.ts +0 -39
- package/dist/utils/reducedMotionConfig.js +0 -92
- package/dist/utils/spring.d.ts +0 -51
- package/dist/utils/spring.js +0 -157
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type FollowValueOptions, type MotionValue, type SpringOptions } from 'motion-dom';
|
|
2
|
+
import { type Readable } from 'svelte/store';
|
|
3
|
+
/**
|
|
4
|
+
* Spring + follow options for {@link useSpring}.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors framer-motion's `useSpring` options 1:1: every `SpringOptions` key
|
|
7
|
+
* (`stiffness`, `damping`, `mass`, `duration`, `visualDuration`, `bounce`,
|
|
8
|
+
* `velocity`, `restDelta`, `restSpeed`) plus `skipInitialAnimation` for
|
|
9
|
+
* scroll-restoration scenarios.
|
|
10
|
+
*
|
|
11
|
+
* @see https://motion.dev/docs/react-use-spring
|
|
12
|
+
*/
|
|
13
|
+
export type UseSpringOptions = SpringOptions & Pick<FollowValueOptions, 'skipInitialAnimation'>;
|
|
14
|
+
/**
|
|
15
|
+
* The augmented `MotionValue` returned by {@link useSpring}.
|
|
16
|
+
*
|
|
17
|
+
* It IS a real `MotionValue<T>` (so it passes `isMotionValue`, composes with
|
|
18
|
+
* `animate()`, `useTransform`, and the rest of motion-dom). On top of that it
|
|
19
|
+
* adds two affordances:
|
|
20
|
+
*
|
|
21
|
+
* - `current` — a Svelte-5 reactive read backed by `$state`. Use in templates
|
|
22
|
+
* and `$derived` / `$effect` to track the spring value without subscribing.
|
|
23
|
+
* - `subscribe` — Svelte readable store contract. Calls the run function once
|
|
24
|
+
* synchronously with the current value, then on every change. Lets the
|
|
25
|
+
* spring be used with `$spring` template syntax, `get(spring)`, and as a
|
|
26
|
+
* dependency in `useTransform`'s function form.
|
|
27
|
+
*/
|
|
28
|
+
export type SpringMotionValue<T extends number | string> = Omit<MotionValue<T>, 'current'> & {
|
|
29
|
+
/** Reactive read in Svelte 5 templates / `$derived` / `$effect`. */
|
|
30
|
+
readonly current: T;
|
|
31
|
+
/** Svelte readable store compatibility. */
|
|
32
|
+
subscribe: (run: (value: T) => void) => () => void;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Creates a spring-animated `MotionValue`.
|
|
36
|
+
*
|
|
37
|
+
* Set a target with `.set(v)` to animate to it using spring physics, or
|
|
38
|
+
* `.jump(v)` to skip the animation. Pass another `MotionValue` (or, for
|
|
39
|
+
* backwards compatibility, a Svelte readable store like the ones from
|
|
40
|
+
* `useScroll` / `useTime`) as `source` and the spring will animate towards
|
|
41
|
+
* whatever that source emits.
|
|
42
|
+
*
|
|
43
|
+
* Returned object is a real motion-dom `MotionValue` — composes with
|
|
44
|
+
* `animate()`, `useTransform`, `useVelocity`, and motion-dom's animation
|
|
45
|
+
* engine. On top, it exposes:
|
|
46
|
+
*
|
|
47
|
+
* - `.current` — Svelte-5 reactive read for templates and `$derived` /
|
|
48
|
+
* `$effect`.
|
|
49
|
+
* - `.subscribe(run)` — Svelte readable store contract so `$spring` template
|
|
50
|
+
* syntax and `useTransform(() => …, [spring])` keep working during the
|
|
51
|
+
* Tier 2 migration window.
|
|
52
|
+
*
|
|
53
|
+
* Lifecycle: must be called during component initialization. Cleanup is
|
|
54
|
+
* registered via `$effect`; the spring stops animating and unsubscribes from
|
|
55
|
+
* its source when the surrounding component / effect tears down. Call
|
|
56
|
+
* `.destroy()` to clean up early.
|
|
57
|
+
*
|
|
58
|
+
* SSR-safe: returns a static `MotionValue` with no animation on the server.
|
|
59
|
+
*
|
|
60
|
+
* @template T
|
|
61
|
+
* @param {number|string|MotionValue<number>|MotionValue<string>|Readable<number|string>} source Initial value or a source to follow.
|
|
62
|
+
* @param {UseSpringOptions} [options] Spring + follow configuration.
|
|
63
|
+
* @returns {SpringMotionValue<T>} A `MotionValue` with `.current` and `.subscribe`.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```svelte
|
|
67
|
+
* <script lang="ts">
|
|
68
|
+
* import { useSpring } from '@humanspeak/svelte-motion'
|
|
69
|
+
*
|
|
70
|
+
* const x = useSpring(0, { stiffness: 300, damping: 30 })
|
|
71
|
+
* </script>
|
|
72
|
+
*
|
|
73
|
+
* <button onclick={() => x.set(100)}>Animate</button>
|
|
74
|
+
* <div>{x.current}</div>
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @see https://motion.dev/docs/react-use-spring
|
|
78
|
+
*/
|
|
79
|
+
export declare function useSpring(source: number, options?: UseSpringOptions): SpringMotionValue<number>;
|
|
80
|
+
export declare function useSpring(source: string, options?: UseSpringOptions): SpringMotionValue<string>;
|
|
81
|
+
export declare function useSpring(source: MotionValue<number>, options?: UseSpringOptions): SpringMotionValue<number>;
|
|
82
|
+
export declare function useSpring(source: MotionValue<string>, options?: UseSpringOptions): SpringMotionValue<string>;
|
|
83
|
+
export declare function useSpring<T extends number | string>(source: Readable<T>, options?: UseSpringOptions): SpringMotionValue<T>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { attachFollow, isMotionValue, motionValue } from 'motion-dom';
|
|
2
|
+
import { get } from 'svelte/store';
|
|
3
|
+
/**
|
|
4
|
+
* Detects a Svelte readable store. Excludes motion-dom `MotionValue` instances
|
|
5
|
+
* (which also expose `subscribe`-shaped APIs in some versions) so the
|
|
6
|
+
* MotionValue path is preferred.
|
|
7
|
+
*/
|
|
8
|
+
const isSvelteReadable = (value) => {
|
|
9
|
+
return (!!value &&
|
|
10
|
+
typeof value === 'object' &&
|
|
11
|
+
typeof value.subscribe === 'function' &&
|
|
12
|
+
!isMotionValue(value));
|
|
13
|
+
};
|
|
14
|
+
export function useSpring(source, options = {}) {
|
|
15
|
+
// SSR: return a static MotionValue with no animation. Reads return the
|
|
16
|
+
// best-effort initial value; .set / .jump become no-ops to avoid drifting
|
|
17
|
+
// away from the server-rendered snapshot.
|
|
18
|
+
if (typeof window === 'undefined') {
|
|
19
|
+
const initial = readInitial(source);
|
|
20
|
+
const ssrValue = motionValue(initial);
|
|
21
|
+
ssrValue.set = () => undefined;
|
|
22
|
+
ssrValue.jump = () => undefined;
|
|
23
|
+
return augmentForSvelte(ssrValue, () => undefined);
|
|
24
|
+
}
|
|
25
|
+
// Resolve initial + follow source.
|
|
26
|
+
let followSource;
|
|
27
|
+
let cleanupReadableBridge;
|
|
28
|
+
let svelteBridge;
|
|
29
|
+
if (isMotionValue(source)) {
|
|
30
|
+
followSource = source;
|
|
31
|
+
}
|
|
32
|
+
else if (isSvelteReadable(source)) {
|
|
33
|
+
// Bridge a Svelte readable into a MotionValue so attachFollow can
|
|
34
|
+
// track it. Synchronous initial sample comes from svelte/store's get().
|
|
35
|
+
const initialFromReadable = get(source);
|
|
36
|
+
svelteBridge = motionValue(initialFromReadable);
|
|
37
|
+
cleanupReadableBridge = source.subscribe((v) => {
|
|
38
|
+
// The Svelte readable contract calls the subscriber synchronously
|
|
39
|
+
// with the current value on subscribe. Skip if it equals the
|
|
40
|
+
// already-seeded bridge value so attachFollow doesn't fire a
|
|
41
|
+
// spring on the initial emit. Subsequent emits go through set()
|
|
42
|
+
// and trigger animation.
|
|
43
|
+
if (svelteBridge.get() === v)
|
|
44
|
+
return;
|
|
45
|
+
svelteBridge.set(v);
|
|
46
|
+
});
|
|
47
|
+
followSource = svelteBridge;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
followSource = source;
|
|
51
|
+
}
|
|
52
|
+
const initial = isMotionValue(followSource) ? followSource.get() : followSource;
|
|
53
|
+
const value = motionValue(initial);
|
|
54
|
+
const stopFollow = attachFollow(value, followSource, { type: 'spring', ...options });
|
|
55
|
+
// Side-cleanup for our augmentations. Single-shot guard lives in the
|
|
56
|
+
// augmented `value.destroy` (the only caller), so no flag here.
|
|
57
|
+
const dispose = () => {
|
|
58
|
+
stopFollow?.();
|
|
59
|
+
cleanupReadableBridge?.();
|
|
60
|
+
svelteBridge?.destroy();
|
|
61
|
+
};
|
|
62
|
+
$effect(() => () => value.destroy());
|
|
63
|
+
return augmentForSvelte(value, dispose);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Pull the synchronous initial value out of any accepted source form.
|
|
67
|
+
*/
|
|
68
|
+
const readInitial = (source) => {
|
|
69
|
+
if (typeof source === 'number' || typeof source === 'string')
|
|
70
|
+
return source;
|
|
71
|
+
if (isMotionValue(source))
|
|
72
|
+
return source.get();
|
|
73
|
+
if (isSvelteReadable(source))
|
|
74
|
+
return get(source);
|
|
75
|
+
return 0;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Layer Svelte-friendly affordances onto a motion-dom MotionValue: a
|
|
79
|
+
* `$state`-tracked `.current` accessor (routing motion-dom's internal
|
|
80
|
+
* `this.current = v` writes through `$state` so templates and `$derived` /
|
|
81
|
+
* `$effect` re-run) and a Svelte readable store `.subscribe(run)` shim.
|
|
82
|
+
*/
|
|
83
|
+
const augmentForSvelte = (value, dispose) => {
|
|
84
|
+
let current = $state(value.get());
|
|
85
|
+
Object.defineProperty(value, 'current', {
|
|
86
|
+
get: () => current,
|
|
87
|
+
// Same-value writes are no-ops: motion-dom's `updateAndNotify` calls
|
|
88
|
+
// `setCurrent(v)` before its own change check, so spring frames at
|
|
89
|
+
// rest still hit this setter; skipping equal writes avoids gratuitous
|
|
90
|
+
// accessor work even though $state would itself dedupe.
|
|
91
|
+
set: (v) => {
|
|
92
|
+
if (v !== current)
|
|
93
|
+
current = v;
|
|
94
|
+
},
|
|
95
|
+
enumerable: true,
|
|
96
|
+
configurable: true
|
|
97
|
+
});
|
|
98
|
+
const originalDestroy = value.destroy.bind(value);
|
|
99
|
+
let destroyed = false;
|
|
100
|
+
value.destroy = () => {
|
|
101
|
+
if (destroyed)
|
|
102
|
+
return;
|
|
103
|
+
destroyed = true;
|
|
104
|
+
dispose();
|
|
105
|
+
originalDestroy();
|
|
106
|
+
};
|
|
107
|
+
const subscribe = (run) => {
|
|
108
|
+
run(value.get());
|
|
109
|
+
return value.on('change', run);
|
|
110
|
+
};
|
|
111
|
+
Object.defineProperty(value, 'subscribe', {
|
|
112
|
+
value: subscribe,
|
|
113
|
+
writable: false,
|
|
114
|
+
enumerable: false,
|
|
115
|
+
configurable: true
|
|
116
|
+
});
|
|
117
|
+
return value;
|
|
118
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
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",
|
|
@@ -95,8 +95,8 @@
|
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
97
|
"acorn": "^8.16.0",
|
|
98
|
-
"motion": "^12.
|
|
99
|
-
"motion-dom": "^12.
|
|
98
|
+
"motion": "^12.40.0",
|
|
99
|
+
"motion-dom": "^12.40.0"
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
102
|
"@changesets/cli": "^2.31.0",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"@testing-library/jest-dom": "^6.9.1",
|
|
116
116
|
"@testing-library/svelte": "^5.3.1",
|
|
117
117
|
"@types/node": "^25.9.1",
|
|
118
|
-
"@vitest/coverage-v8": "^4.1.
|
|
118
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
119
119
|
"eslint": "^10.4.0",
|
|
120
120
|
"eslint-config-prettier": "10.1.8",
|
|
121
121
|
"eslint-plugin-import": "2.32.0",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"html-void-elements": "^3.0.0",
|
|
128
128
|
"husky": "^9.1.7",
|
|
129
129
|
"jsdom": "^29.1.1",
|
|
130
|
-
"mprocs": "^0.9.
|
|
130
|
+
"mprocs": "^0.9.3",
|
|
131
131
|
"prettier": "^3.8.3",
|
|
132
132
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
133
133
|
"prettier-plugin-sort-json": "^4.2.0",
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"prettier-plugin-tailwindcss": "^0.8.0",
|
|
136
136
|
"publint": "^0.3.21",
|
|
137
137
|
"runed": "0.37.1",
|
|
138
|
-
"svelte": "^5.55.
|
|
138
|
+
"svelte": "^5.55.9",
|
|
139
139
|
"svelte-check": "^4.4.8",
|
|
140
140
|
"svg-tags": "^1.0.0",
|
|
141
141
|
"tailwind-merge": "^3.6.0",
|
|
@@ -145,9 +145,9 @@
|
|
|
145
145
|
"tsx": "^4.22.3",
|
|
146
146
|
"typescript": "^6.0.3",
|
|
147
147
|
"typescript-eslint": "^8.59.4",
|
|
148
|
-
"vite": "^8.0.
|
|
148
|
+
"vite": "^8.0.14",
|
|
149
149
|
"vite-tsconfig-paths": "^6.1.1",
|
|
150
|
-
"vitest": "^4.1.
|
|
150
|
+
"vitest": "^4.1.7"
|
|
151
151
|
},
|
|
152
152
|
"peerDependencies": {
|
|
153
153
|
"svelte": "^5.0.0"
|
package/dist/utils/cycle.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { type Readable } from 'svelte/store';
|
|
2
|
-
export type Cycle = (next?: number) => void;
|
|
3
|
-
export type CycleState<T> = [Readable<T>, Cycle];
|
|
4
|
-
/**
|
|
5
|
-
* Cycles through a series of values. Mirrors Framer Motion's `useCycle`.
|
|
6
|
-
*
|
|
7
|
-
* Returns a tuple `[value, cycle]`:
|
|
8
|
-
*
|
|
9
|
-
* - `value` is a Svelte readable store of the current item; subscribe with
|
|
10
|
-
* `$value` in templates.
|
|
11
|
-
* - `cycle()` advances to the next item, wrapping back to index `0` when it
|
|
12
|
-
* passes the end.
|
|
13
|
-
* - `cycle(i)` jumps to the item at index `i`. The index is taken as-is to
|
|
14
|
-
* match `framer-motion` — out-of-range values yield `items[i]`, which
|
|
15
|
-
* may be `undefined`.
|
|
16
|
-
*
|
|
17
|
-
* Calls that resolve to the current index are no-ops and do not notify
|
|
18
|
-
* subscribers, matching React `useState`'s `Object.is` bail-out semantics.
|
|
19
|
-
*
|
|
20
|
-
* @param items - Items to cycle through. Must include at least one item.
|
|
21
|
-
* @returns A `[Readable<T>, Cycle]` tuple.
|
|
22
|
-
* @see https://motion.dev/docs/react-use-cycle
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```svelte
|
|
26
|
-
* <script>
|
|
27
|
-
* import { motion, useCycle } from '@humanspeak/svelte-motion'
|
|
28
|
-
*
|
|
29
|
-
* const [x, cycleX] = useCycle(0, 50, 100)
|
|
30
|
-
* </script>
|
|
31
|
-
*
|
|
32
|
-
* <motion.div animate={{ x: $x }} onclick={() => cycleX()} />
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export declare const useCycle: <T>(...items: T[]) => CycleState<T>;
|
package/dist/utils/cycle.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { wrap } from 'motion';
|
|
2
|
-
import { writable } from 'svelte/store';
|
|
3
|
-
/**
|
|
4
|
-
* Cycles through a series of values. Mirrors Framer Motion's `useCycle`.
|
|
5
|
-
*
|
|
6
|
-
* Returns a tuple `[value, cycle]`:
|
|
7
|
-
*
|
|
8
|
-
* - `value` is a Svelte readable store of the current item; subscribe with
|
|
9
|
-
* `$value` in templates.
|
|
10
|
-
* - `cycle()` advances to the next item, wrapping back to index `0` when it
|
|
11
|
-
* passes the end.
|
|
12
|
-
* - `cycle(i)` jumps to the item at index `i`. The index is taken as-is to
|
|
13
|
-
* match `framer-motion` — out-of-range values yield `items[i]`, which
|
|
14
|
-
* may be `undefined`.
|
|
15
|
-
*
|
|
16
|
-
* Calls that resolve to the current index are no-ops and do not notify
|
|
17
|
-
* subscribers, matching React `useState`'s `Object.is` bail-out semantics.
|
|
18
|
-
*
|
|
19
|
-
* @param items - Items to cycle through. Must include at least one item.
|
|
20
|
-
* @returns A `[Readable<T>, Cycle]` tuple.
|
|
21
|
-
* @see https://motion.dev/docs/react-use-cycle
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```svelte
|
|
25
|
-
* <script>
|
|
26
|
-
* import { motion, useCycle } from '@humanspeak/svelte-motion'
|
|
27
|
-
*
|
|
28
|
-
* const [x, cycleX] = useCycle(0, 50, 100)
|
|
29
|
-
* </script>
|
|
30
|
-
*
|
|
31
|
-
* <motion.div animate={{ x: $x }} onclick={() => cycleX()} />
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
export const useCycle = (...items) => {
|
|
35
|
-
if (items.length === 0) {
|
|
36
|
-
throw new Error('useCycle requires at least one item');
|
|
37
|
-
}
|
|
38
|
-
let index = 0;
|
|
39
|
-
const store = writable(items[0]);
|
|
40
|
-
const cycle = (next) => {
|
|
41
|
-
const target = typeof next === 'number' ? next : wrap(0, items.length, index + 1);
|
|
42
|
-
if (target === index)
|
|
43
|
-
return;
|
|
44
|
-
index = target;
|
|
45
|
-
store.set(items[target]);
|
|
46
|
-
};
|
|
47
|
-
return [{ subscribe: store.subscribe }, cycle];
|
|
48
|
-
};
|
package/dist/utils/inView.d.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { type AnimationOptions, type DOMKeyframesDefinition } from 'motion';
|
|
2
|
-
import { type Readable } from 'svelte/store';
|
|
3
|
-
import type { MotionViewport } from '../types.js';
|
|
4
|
-
import { type ElementOrGetter } from './dom.js';
|
|
5
|
-
/**
|
|
6
|
-
* Split a whileInView definition into keyframes and an optional nested transition.
|
|
7
|
-
*
|
|
8
|
-
* @param def While-in-view record that may include a nested `transition`.
|
|
9
|
-
* @returns Object with `keyframes` (no `transition`) and optional `transition`.
|
|
10
|
-
* @example
|
|
11
|
-
* // With transition
|
|
12
|
-
* splitInViewDefinition({ opacity: 1, y: 0, transition: { duration: 0.5 } })
|
|
13
|
-
* // => { keyframes: { opacity: 1, y: 0 }, transition: { duration: 0.5 } }
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* // Without transition
|
|
17
|
-
* splitInViewDefinition({ opacity: 1, scale: 1 })
|
|
18
|
-
* // => { keyframes: { opacity: 1, scale: 1 }, transition: undefined }
|
|
19
|
-
*/
|
|
20
|
-
export declare const splitInViewDefinition: (def: Record<string, unknown>) => {
|
|
21
|
-
keyframes: Record<string, unknown>;
|
|
22
|
-
transition?: AnimationOptions;
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Compute the baseline values to restore to when element leaves viewport.
|
|
26
|
-
*
|
|
27
|
-
* Preference order per key: `animate` → `initial` → neutral transform defaults
|
|
28
|
-
* → computed style value if present.
|
|
29
|
-
*
|
|
30
|
-
* @param el Target element.
|
|
31
|
-
* @param opts Source records for baseline computation.
|
|
32
|
-
* @returns Minimal baseline record to restore when element leaves viewport.
|
|
33
|
-
* @example
|
|
34
|
-
* computeInViewBaseline(element, {
|
|
35
|
-
* initial: { opacity: 0, y: 50 },
|
|
36
|
-
* animate: { opacity: 1, y: 0 },
|
|
37
|
-
* whileInView: { opacity: 1, scale: 1.1 }
|
|
38
|
-
* })
|
|
39
|
-
* // => { opacity: 1, scale: 1 } (scale defaults to 1, opacity from animate)
|
|
40
|
-
*/
|
|
41
|
-
export declare const computeInViewBaseline: (el: HTMLElement, opts: {
|
|
42
|
-
initial?: Record<string, unknown>;
|
|
43
|
-
animate?: Record<string, unknown>;
|
|
44
|
-
whileInView?: Record<string, unknown>;
|
|
45
|
-
}) => Record<string, unknown>;
|
|
46
|
-
/**
|
|
47
|
-
* Attach whileInView interactions to an element via motion's `inView` primitive.
|
|
48
|
-
*
|
|
49
|
-
* On entry, animates to `whileInView` state (using the nested `transition` if
|
|
50
|
-
* provided). On exit, restores the changed keys to a baseline computed from
|
|
51
|
-
* `initial` / `animate` / neutral transform defaults / inline styles.
|
|
52
|
-
*
|
|
53
|
-
* Delegates to motion's `inView` so the IntersectionObserver implementation
|
|
54
|
-
* is shared with the public `useInView` hook.
|
|
55
|
-
*
|
|
56
|
-
* @param el Target element.
|
|
57
|
-
* @param whileInView While-in-view definition.
|
|
58
|
-
* @param mergedTransition Root/component merged transition.
|
|
59
|
-
* @param callbacks Optional lifecycle callbacks for in-view start/end and animation completion.
|
|
60
|
-
* @param baselineSources Optional sources used to compute baseline.
|
|
61
|
-
* @param viewport Optional IntersectionObserver options. `amount` defaults to `0` (any pixel visible).
|
|
62
|
-
* @returns Cleanup function to stop observing.
|
|
63
|
-
* @example
|
|
64
|
-
* const cleanup = attachWhileInView(
|
|
65
|
-
* element,
|
|
66
|
-
* { opacity: 1, y: 0, transition: { duration: 0.5 } },
|
|
67
|
-
* { duration: 0.3 },
|
|
68
|
-
* {
|
|
69
|
-
* onStart: () => console.log('Entered viewport'),
|
|
70
|
-
* onEnd: () => console.log('Left viewport')
|
|
71
|
-
* },
|
|
72
|
-
* { initial: { opacity: 0, y: 50 } },
|
|
73
|
-
* { once: true, amount: 0.5 }
|
|
74
|
-
* )
|
|
75
|
-
* // Later: cleanup() to stop observing
|
|
76
|
-
*/
|
|
77
|
-
export declare const attachWhileInView: (el: HTMLElement, whileInView: Record<string, unknown> | undefined, mergedTransition: AnimationOptions, callbacks?: {
|
|
78
|
-
onStart?: () => void;
|
|
79
|
-
onEnd?: () => void;
|
|
80
|
-
onAnimationComplete?: (definition: DOMKeyframesDefinition | undefined) => void;
|
|
81
|
-
}, baselineSources?: {
|
|
82
|
-
initial?: Record<string, unknown>;
|
|
83
|
-
animate?: Record<string, unknown>;
|
|
84
|
-
}, viewport?: MotionViewport) => (() => void);
|
|
85
|
-
/**
|
|
86
|
-
* Options accepted by `useInView`.
|
|
87
|
-
*/
|
|
88
|
-
export type UseInViewOptions = {
|
|
89
|
-
/** Element to use as the IntersectionObserver root. Defaults to the viewport. */
|
|
90
|
-
root?: ElementOrGetter;
|
|
91
|
-
/** CSS margin string applied to the root bounding box (e.g. `"100px 0px"`). */
|
|
92
|
-
margin?: string;
|
|
93
|
-
/** Fraction (0-1) or `"some"` / `"all"` of the target that must be visible. */
|
|
94
|
-
amount?: 'some' | 'all' | number;
|
|
95
|
-
/** When `true`, the store latches to `true` on first entry and never flips back. */
|
|
96
|
-
once?: boolean;
|
|
97
|
-
/** Initial value emitted before the first IntersectionObserver callback. */
|
|
98
|
-
initial?: boolean;
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Returns a Svelte readable store that tracks whether `target` is in the
|
|
102
|
-
* viewport. Mirrors Framer Motion's `useInView` so the same options
|
|
103
|
-
* (`root`, `margin`, `amount`, `once`, `initial`) work as in React.
|
|
104
|
-
*
|
|
105
|
-
* `target` (and `options.root`) accept either an `HTMLElement` directly or a
|
|
106
|
-
* getter `() => HTMLElement | undefined`. With Svelte's `bind:this`, the
|
|
107
|
-
* element isn't available until after mount, so element resolution is
|
|
108
|
-
* deferred until the first subscriber arrives; if the element isn't ready,
|
|
109
|
-
* the hook polls on `requestAnimationFrame` until it is.
|
|
110
|
-
*
|
|
111
|
-
* SSR-safe: returns a static `readable(initial)` when `window` or
|
|
112
|
-
* `IntersectionObserver` is unavailable.
|
|
113
|
-
*
|
|
114
|
-
* @param target - Element (or getter) to observe.
|
|
115
|
-
* @param options - Optional `UseInViewOptions` (`root`, `margin`, `amount`,
|
|
116
|
-
* `once`, `initial`).
|
|
117
|
-
* @returns A `Readable<boolean>` that flips to `true` while `target` is in view.
|
|
118
|
-
* @see https://motion.dev/docs/react-use-in-view
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* ```svelte
|
|
122
|
-
* <script>
|
|
123
|
-
* import { useInView } from '@humanspeak/svelte-motion'
|
|
124
|
-
*
|
|
125
|
-
* let ref
|
|
126
|
-
* const inView = useInView(() => ref, { once: true })
|
|
127
|
-
*
|
|
128
|
-
* $effect(() => {
|
|
129
|
-
* if ($inView) trackImpression()
|
|
130
|
-
* })
|
|
131
|
-
* </script>
|
|
132
|
-
*
|
|
133
|
-
* <div bind:this={ref}>{$inView ? 'visible' : 'hidden'}</div>
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
export declare const useInView: (target: ElementOrGetter, options?: UseInViewOptions) => Readable<boolean>;
|