@humanspeak/svelte-motion 0.1.22 → 0.1.24
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/README.md +2 -0
- package/dist/html/_MotionContainer.svelte +20 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/utils/motionTemplate.d.ts +21 -0
- package/dist/utils/motionTemplate.js +33 -0
- package/dist/utils/velocity.d.ts +15 -0
- package/dist/utils/velocity.js +62 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -231,9 +231,11 @@ Single-element FLIP layout animation:
|
|
|
231
231
|
## Utilities
|
|
232
232
|
|
|
233
233
|
- `useAnimationFrame`
|
|
234
|
+
- `useMotionTemplate`
|
|
234
235
|
- `useSpring`
|
|
235
236
|
- `useTime`
|
|
236
237
|
- `useTransform`
|
|
238
|
+
- `useVelocity`
|
|
237
239
|
- `styleString`
|
|
238
240
|
- `stringifyStyleObject` (deprecated)
|
|
239
241
|
- `createDragControls`
|
|
@@ -140,10 +140,10 @@
|
|
|
140
140
|
const presenceKey = keyProp ?? `motion-${++keyCounter}`
|
|
141
141
|
|
|
142
142
|
// Track previous key for key-change detection (simulates React's key-based remounting)
|
|
143
|
-
//
|
|
144
|
-
let keyTrackerPrev =
|
|
145
|
-
let keyTrackerIsTransitioning =
|
|
146
|
-
let keyTransitionStopped =
|
|
143
|
+
// Plain variables (not $state) to avoid self-triggering the key-change $effect
|
|
144
|
+
let keyTrackerPrev = keyProp
|
|
145
|
+
let keyTrackerIsTransitioning = false
|
|
146
|
+
let keyTransitionStopped = false
|
|
147
147
|
|
|
148
148
|
// Compute merged transition without mutating props to avoid effect write loops
|
|
149
149
|
const mergedTransition = $derived<AnimationOptions>(
|
|
@@ -699,6 +699,14 @@
|
|
|
699
699
|
currentKey === keyTrackerPrev ||
|
|
700
700
|
keyTrackerPrev === undefined
|
|
701
701
|
) {
|
|
702
|
+
pwLog('[motion] key effect: early return', {
|
|
703
|
+
currentKey,
|
|
704
|
+
keyTrackerPrev,
|
|
705
|
+
isLoaded,
|
|
706
|
+
hasElement: !!element,
|
|
707
|
+
hasContext: !!context,
|
|
708
|
+
keyTrackerIsTransitioning
|
|
709
|
+
})
|
|
702
710
|
// Update prev for next comparison
|
|
703
711
|
if (currentKey !== keyTrackerPrev) {
|
|
704
712
|
keyTrackerPrev = currentKey
|
|
@@ -713,6 +721,7 @@
|
|
|
713
721
|
|
|
714
722
|
// Mark as transitioning to prevent re-entry
|
|
715
723
|
keyTrackerIsTransitioning = true
|
|
724
|
+
keyTransitionStopped = false
|
|
716
725
|
keyTrackerPrev = currentKey
|
|
717
726
|
|
|
718
727
|
// Run the key transition sequence
|
|
@@ -732,6 +741,11 @@
|
|
|
732
741
|
).finished
|
|
733
742
|
}
|
|
734
743
|
|
|
744
|
+
pwLog('[motion] key transition: exit done', {
|
|
745
|
+
keyTransitionStopped,
|
|
746
|
+
hasElement: !!element
|
|
747
|
+
})
|
|
748
|
+
|
|
735
749
|
// Check if component was unmounted during exit animation
|
|
736
750
|
if (keyTransitionStopped || !element) return
|
|
737
751
|
|
|
@@ -752,6 +766,7 @@
|
|
|
752
766
|
pwLog('[motion] key transition: running enter animation')
|
|
753
767
|
runAnimation()
|
|
754
768
|
} finally {
|
|
769
|
+
pwLog('[motion] key transition: finally', { keyTransitionStopped })
|
|
755
770
|
if (!keyTransitionStopped) {
|
|
756
771
|
keyTrackerIsTransitioning = false
|
|
757
772
|
}
|
|
@@ -762,6 +777,7 @@
|
|
|
762
777
|
|
|
763
778
|
// Cleanup on unmount
|
|
764
779
|
return () => {
|
|
780
|
+
pwLog('[motion] key effect: cleanup, stopping transition')
|
|
765
781
|
keyTransitionStopped = true
|
|
766
782
|
}
|
|
767
783
|
})
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,9 @@ export { clamp, distance, distance2D, interpolate, mix, pipe, progress, wrap } f
|
|
|
8
8
|
export type { DragAxis, DragConstraints, DragControls, DragInfo, DragTransition, MotionAnimate, MotionInitial, MotionOnDirectionLock, MotionOnDragTransitionEnd, MotionOnInViewEnd, MotionOnInViewStart, MotionTransition, MotionWhileDrag, MotionWhileFocus, MotionWhileHover, MotionWhileInView, MotionWhileTap, Variants } from './types';
|
|
9
9
|
export { useAnimationFrame } from './utils/animationFrame';
|
|
10
10
|
export { createDragControls } from './utils/dragControls';
|
|
11
|
+
export { useMotionTemplate } from './utils/motionTemplate';
|
|
11
12
|
export { useSpring } from './utils/spring';
|
|
13
|
+
export { useVelocity } from './utils/velocity';
|
|
12
14
|
/**
|
|
13
15
|
* @deprecated Use `styleString` instead for reactive styles with automatic unit handling.
|
|
14
16
|
*/
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,9 @@ export { anticipate, backIn, backInOut, backOut, circIn, circInOut, circOut, cub
|
|
|
11
11
|
export { clamp, distance, distance2D, interpolate, mix, pipe, progress, wrap } from 'motion';
|
|
12
12
|
export { useAnimationFrame } from './utils/animationFrame';
|
|
13
13
|
export { createDragControls } from './utils/dragControls';
|
|
14
|
+
export { useMotionTemplate } from './utils/motionTemplate';
|
|
14
15
|
export { useSpring } from './utils/spring';
|
|
16
|
+
export { useVelocity } from './utils/velocity';
|
|
15
17
|
/**
|
|
16
18
|
* @deprecated Use `styleString` instead for reactive styles with automatic unit handling.
|
|
17
19
|
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
/**
|
|
3
|
+
* Tagged template literal that creates a derived store from interpolated
|
|
4
|
+
* readable stores. When any input store changes, the resulting store
|
|
5
|
+
* recomputes the template string.
|
|
6
|
+
*
|
|
7
|
+
* SSR-safe: `derived` from svelte/store works on the server.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const blur = useSpring(0)
|
|
12
|
+
* const filter = useMotionTemplate`blur(${blur}px)`
|
|
13
|
+
* // $filter → "blur(0px)"
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @param strings Static template string parts.
|
|
17
|
+
* @param values Readable stores to interpolate.
|
|
18
|
+
* @returns A readable store of the composed string.
|
|
19
|
+
* @see https://motion.dev/docs/react-use-motion-template
|
|
20
|
+
*/
|
|
21
|
+
export declare const useMotionTemplate: (strings: TemplateStringsArray, ...values: Readable<number | string>[]) => Readable<string>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { derived } from 'svelte/store';
|
|
2
|
+
/**
|
|
3
|
+
* Tagged template literal that creates a derived store from interpolated
|
|
4
|
+
* readable stores. When any input store changes, the resulting store
|
|
5
|
+
* recomputes the template string.
|
|
6
|
+
*
|
|
7
|
+
* SSR-safe: `derived` from svelte/store works on the server.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const blur = useSpring(0)
|
|
12
|
+
* const filter = useMotionTemplate`blur(${blur}px)`
|
|
13
|
+
* // $filter → "blur(0px)"
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @param strings Static template string parts.
|
|
17
|
+
* @param values Readable stores to interpolate.
|
|
18
|
+
* @returns A readable store of the composed string.
|
|
19
|
+
* @see https://motion.dev/docs/react-use-motion-template
|
|
20
|
+
*/
|
|
21
|
+
export const useMotionTemplate = (strings, ...values) => {
|
|
22
|
+
if (values.length === 0)
|
|
23
|
+
return derived([], () => strings[0] ?? '');
|
|
24
|
+
return derived(values, (current) => {
|
|
25
|
+
let result = '';
|
|
26
|
+
for (let i = 0; i < strings.length; i++) {
|
|
27
|
+
result += strings[i] ?? '';
|
|
28
|
+
if (i < current.length)
|
|
29
|
+
result += String(current[i]);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a readable store that tracks the velocity of a source store's value.
|
|
4
|
+
*
|
|
5
|
+
* Uses `motionValue` from motion-dom for built-in velocity tracking with
|
|
6
|
+
* timestamps. Polls velocity via `requestAnimationFrame` and settles to 0
|
|
7
|
+
* when movement stops.
|
|
8
|
+
*
|
|
9
|
+
* SSR-safe: returns a static `readable(0)` on the server.
|
|
10
|
+
*
|
|
11
|
+
* @param source A readable store of numeric or unit-string values.
|
|
12
|
+
* @returns A readable store of the current velocity in units/second.
|
|
13
|
+
* @see https://motion.dev/docs/react-use-velocity
|
|
14
|
+
*/
|
|
15
|
+
export declare const useVelocity: (source: Readable<number | string>) => Readable<number>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { motionValue } from 'motion-dom';
|
|
2
|
+
import { readable, writable } from 'svelte/store';
|
|
3
|
+
/**
|
|
4
|
+
* Parses a numeric value from a number or unit string (e.g. "100px" → 100).
|
|
5
|
+
*/
|
|
6
|
+
const parseNumeric = (v) => {
|
|
7
|
+
if (typeof v === 'number')
|
|
8
|
+
return v;
|
|
9
|
+
const parsed = Number.parseFloat(String(v));
|
|
10
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Creates a readable store that tracks the velocity of a source store's value.
|
|
14
|
+
*
|
|
15
|
+
* Uses `motionValue` from motion-dom for built-in velocity tracking with
|
|
16
|
+
* timestamps. Polls velocity via `requestAnimationFrame` and settles to 0
|
|
17
|
+
* when movement stops.
|
|
18
|
+
*
|
|
19
|
+
* SSR-safe: returns a static `readable(0)` on the server.
|
|
20
|
+
*
|
|
21
|
+
* @param source A readable store of numeric or unit-string values.
|
|
22
|
+
* @returns A readable store of the current velocity in units/second.
|
|
23
|
+
* @see https://motion.dev/docs/react-use-velocity
|
|
24
|
+
*/
|
|
25
|
+
export const useVelocity = (source) => {
|
|
26
|
+
if (typeof window === 'undefined')
|
|
27
|
+
return readable(0, () => { });
|
|
28
|
+
const mv = motionValue(0);
|
|
29
|
+
const store = writable(0);
|
|
30
|
+
let raf = 0;
|
|
31
|
+
let settled = true;
|
|
32
|
+
const poll = () => {
|
|
33
|
+
const v = mv.getVelocity();
|
|
34
|
+
store.set(v);
|
|
35
|
+
if (Math.abs(v) < 0.001) {
|
|
36
|
+
settled = true;
|
|
37
|
+
store.set(0);
|
|
38
|
+
raf = 0;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
raf = requestAnimationFrame(poll);
|
|
42
|
+
};
|
|
43
|
+
const startPolling = () => {
|
|
44
|
+
if (!settled)
|
|
45
|
+
return;
|
|
46
|
+
settled = false;
|
|
47
|
+
raf = requestAnimationFrame(poll);
|
|
48
|
+
};
|
|
49
|
+
return readable(0, (set) => {
|
|
50
|
+
const unsubStore = store.subscribe(set);
|
|
51
|
+
const unsubSource = source.subscribe((v) => {
|
|
52
|
+
mv.set(parseNumeric(v));
|
|
53
|
+
startPolling();
|
|
54
|
+
});
|
|
55
|
+
return () => {
|
|
56
|
+
unsubStore();
|
|
57
|
+
unsubSource();
|
|
58
|
+
if (raf)
|
|
59
|
+
cancelAnimationFrame(raf);
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"description": "A lightweight animation library for Svelte 5 that provides smooth, hardware-accelerated animations. Features include spring physics, custom easing, and fluid transitions. Built on top of the motion library, it offers a simple API for creating complex animations with minimal code. Perfect for interactive UIs, micro-interactions, and engaging user experiences.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"svelte": "^5.0.0"
|
|
112
112
|
},
|
|
113
113
|
"volta": {
|
|
114
|
-
"node": "
|
|
114
|
+
"node": "22.16.0"
|
|
115
115
|
},
|
|
116
116
|
"publishConfig": {
|
|
117
117
|
"access": "public"
|