@humanspeak/svelte-motion 0.1.25 → 0.1.27
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/components/motionConfig.context.d.ts +11 -0
- package/dist/components/motionConfig.context.js +11 -0
- package/dist/components/variantContext.context.d.ts +14 -6
- package/dist/components/variantContext.context.js +18 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/utils/animationFrame.d.ts +1 -1
- package/dist/utils/animationFrame.js +2 -2
- package/dist/utils/dragMath.d.ts +18 -1
- package/dist/utils/dragMath.js +17 -2
- package/dist/utils/dragParams.d.ts +2 -2
- package/dist/utils/dragParams.js +2 -2
- package/dist/utils/inertia.d.ts +24 -1
- package/dist/utils/inertia.js +17 -2
- package/dist/utils/layoutId.d.ts +10 -1
- package/dist/utils/layoutId.js +11 -2
- package/dist/utils/log.d.ts +15 -0
- package/dist/utils/log.js +15 -0
- package/dist/utils/motionValueEvent.d.ts +29 -0
- package/dist/utils/motionValueEvent.js +38 -0
- package/dist/utils/presence.d.ts +5 -5
- package/dist/utils/presence.js +8 -8
- package/dist/utils/scroll.d.ts +68 -0
- package/dist/utils/scroll.js +119 -0
- package/dist/utils/testing.d.ts +11 -0
- package/dist/utils/testing.js +11 -0
- package/dist/utils/transform.d.ts +23 -6
- package/dist/utils/transform.js +33 -11
- package/package.json +7 -7
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
import type { MotionConfigProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Retrieve the current motion configuration from Svelte component context.
|
|
4
|
+
*
|
|
5
|
+
* @returns The active `MotionConfigProps`, or `undefined` if none was set by a parent.
|
|
6
|
+
*/
|
|
2
7
|
export declare const getMotionConfig: () => MotionConfigProps | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Provide motion configuration to descendant components via Svelte context.
|
|
10
|
+
*
|
|
11
|
+
* @param motionConfig The configuration to propagate (e.g. `transition`).
|
|
12
|
+
* @returns The same `MotionConfigProps` that was set.
|
|
13
|
+
*/
|
|
3
14
|
export declare const createMotionConfig: (motionConfig: MotionConfigProps) => MotionConfigProps;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
const key = Symbol('motionConfig');
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve the current motion configuration from Svelte component context.
|
|
5
|
+
*
|
|
6
|
+
* @returns The active `MotionConfigProps`, or `undefined` if none was set by a parent.
|
|
7
|
+
*/
|
|
3
8
|
export const getMotionConfig = () => {
|
|
4
9
|
return getContext(key);
|
|
5
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Provide motion configuration to descendant components via Svelte context.
|
|
13
|
+
*
|
|
14
|
+
* @param motionConfig The configuration to propagate (e.g. `transition`).
|
|
15
|
+
* @returns The same `MotionConfigProps` that was set.
|
|
16
|
+
*/
|
|
6
17
|
export const createMotionConfig = (motionConfig) => {
|
|
7
18
|
return setContext(key, motionConfig);
|
|
8
19
|
};
|
|
@@ -2,18 +2,26 @@ import type { Writable } from 'svelte/store';
|
|
|
2
2
|
/**
|
|
3
3
|
* Provide a writable store for the current variant key so children can
|
|
4
4
|
* react to changes over time (true inheritance like Framer Motion).
|
|
5
|
+
*
|
|
6
|
+
* @param store Writable store holding the current variant name.
|
|
5
7
|
*/
|
|
6
|
-
export declare
|
|
8
|
+
export declare const setVariantContext: (store: Writable<string | undefined>) => void;
|
|
7
9
|
/**
|
|
8
10
|
* Read the parent's variant store (if any). Children subscribe to this store
|
|
9
11
|
* to inherit and react to parent `animate` changes.
|
|
12
|
+
*
|
|
13
|
+
* @returns The parent variant store, or `undefined` if none exists.
|
|
10
14
|
*/
|
|
11
|
-
export declare
|
|
15
|
+
export declare const getVariantContext: () => Writable<string | undefined> | undefined;
|
|
12
16
|
/**
|
|
13
|
-
* Set initial={false} in context so children inherit it
|
|
17
|
+
* Set initial={false} in context so children inherit it.
|
|
18
|
+
*
|
|
19
|
+
* @param value Whether the parent has `initial={false}`.
|
|
14
20
|
*/
|
|
15
|
-
export declare
|
|
21
|
+
export declare const setInitialFalseContext: (value: boolean) => void;
|
|
16
22
|
/**
|
|
17
|
-
* Check if parent has initial={false}
|
|
23
|
+
* Check if parent has initial={false}.
|
|
24
|
+
*
|
|
25
|
+
* @returns `true` if a parent set `initial={false}`, otherwise `false`.
|
|
18
26
|
*/
|
|
19
|
-
export declare
|
|
27
|
+
export declare const getInitialFalseContext: () => boolean;
|
|
@@ -4,26 +4,34 @@ const INITIAL_FALSE_CONTEXT_KEY = Symbol('initial-false-context');
|
|
|
4
4
|
/**
|
|
5
5
|
* Provide a writable store for the current variant key so children can
|
|
6
6
|
* react to changes over time (true inheritance like Framer Motion).
|
|
7
|
+
*
|
|
8
|
+
* @param store Writable store holding the current variant name.
|
|
7
9
|
*/
|
|
8
|
-
export
|
|
10
|
+
export const setVariantContext = (store) => {
|
|
9
11
|
setContext(VARIANT_CONTEXT_KEY, store);
|
|
10
|
-
}
|
|
12
|
+
};
|
|
11
13
|
/**
|
|
12
14
|
* Read the parent's variant store (if any). Children subscribe to this store
|
|
13
15
|
* to inherit and react to parent `animate` changes.
|
|
16
|
+
*
|
|
17
|
+
* @returns The parent variant store, or `undefined` if none exists.
|
|
14
18
|
*/
|
|
15
|
-
export
|
|
19
|
+
export const getVariantContext = () => {
|
|
16
20
|
return getContext(VARIANT_CONTEXT_KEY);
|
|
17
|
-
}
|
|
21
|
+
};
|
|
18
22
|
/**
|
|
19
|
-
* Set initial={false} in context so children inherit it
|
|
23
|
+
* Set initial={false} in context so children inherit it.
|
|
24
|
+
*
|
|
25
|
+
* @param value Whether the parent has `initial={false}`.
|
|
20
26
|
*/
|
|
21
|
-
export
|
|
27
|
+
export const setInitialFalseContext = (value) => {
|
|
22
28
|
setContext(INITIAL_FALSE_CONTEXT_KEY, value);
|
|
23
|
-
}
|
|
29
|
+
};
|
|
24
30
|
/**
|
|
25
|
-
* Check if parent has initial={false}
|
|
31
|
+
* Check if parent has initial={false}.
|
|
32
|
+
*
|
|
33
|
+
* @returns `true` if a parent set `initial={false}`, otherwise `false`.
|
|
26
34
|
*/
|
|
27
|
-
export
|
|
35
|
+
export const getInitialFalseContext = () => {
|
|
28
36
|
return getContext(INITIAL_FALSE_CONTEXT_KEY) ?? false;
|
|
29
|
-
}
|
|
37
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type { DragAxis, DragConstraints, DragControls, DragInfo, DragTransition,
|
|
|
9
9
|
export { useAnimationFrame } from './utils/animationFrame';
|
|
10
10
|
export { createDragControls } from './utils/dragControls';
|
|
11
11
|
export { useMotionTemplate } from './utils/motionTemplate';
|
|
12
|
+
export { useMotionValueEvent } from './utils/motionValueEvent';
|
|
13
|
+
export { useScroll } from './utils/scroll';
|
|
12
14
|
export { useSpring } from './utils/spring';
|
|
13
15
|
export { useVelocity } from './utils/velocity';
|
|
14
16
|
/**
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ export { clamp, distance, distance2D, interpolate, mix, pipe, progress, wrap } f
|
|
|
12
12
|
export { useAnimationFrame } from './utils/animationFrame';
|
|
13
13
|
export { createDragControls } from './utils/dragControls';
|
|
14
14
|
export { useMotionTemplate } from './utils/motionTemplate';
|
|
15
|
+
export { useMotionValueEvent } from './utils/motionValueEvent';
|
|
16
|
+
export { useScroll } from './utils/scroll';
|
|
15
17
|
export { useSpring } from './utils/spring';
|
|
16
18
|
export { useVelocity } from './utils/velocity';
|
|
17
19
|
/**
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
* <div bind:this={ref}>Animated content</div>
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
-
export declare
|
|
35
|
+
export declare const useAnimationFrame: (callback: (time: DOMHighResTimeStamp) => void) => (() => void);
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* <div bind:this={ref}>Animated content</div>
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
-
export
|
|
35
|
+
export const useAnimationFrame = (callback) => {
|
|
36
36
|
// SSR guard
|
|
37
37
|
if (typeof window === 'undefined')
|
|
38
38
|
return () => { };
|
|
@@ -51,4 +51,4 @@ export function useAnimationFrame(callback) {
|
|
|
51
51
|
cancelAnimationFrame(rafId);
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
}
|
|
54
|
+
};
|
package/dist/utils/dragMath.d.ts
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
* Preserves float precision.
|
|
4
4
|
*/
|
|
5
5
|
export declare const mixNumber: (from: number, to: number, t: number) => number;
|
|
6
|
+
/** Range with optional min/max boundaries for constraining a point. */
|
|
6
7
|
export type ConstraintRange = {
|
|
7
8
|
min?: number;
|
|
8
9
|
max?: number;
|
|
9
10
|
};
|
|
11
|
+
/** Per-side elastic factors, a uniform factor, or `undefined` for no elasticity. */
|
|
10
12
|
export type ConstraintElastic = {
|
|
11
13
|
min: number;
|
|
12
14
|
max: number;
|
|
@@ -15,5 +17,20 @@ export type ConstraintElastic = {
|
|
|
15
17
|
* Apply float-safe constraints with optional elastic mixing.
|
|
16
18
|
* Mirrors Framer Motion behavior: clamp via Math.min/Math.max with no rounding.
|
|
17
19
|
* If `elastic` provided, blends toward the bound using its side-specific factor.
|
|
20
|
+
*
|
|
21
|
+
* @param point The unconstrained value to clamp.
|
|
22
|
+
* @param range Min/max boundaries. Either or both may be omitted.
|
|
23
|
+
* @param elastic Optional per-side elastic factor(s) in [0,1]. When provided,
|
|
24
|
+
* the value is interpolated toward the boundary instead of hard-clamped.
|
|
25
|
+
* @returns The constrained (and optionally elastically blended) value.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Hard clamp to [0, 100]
|
|
30
|
+
* applyConstraints(120, { min: 0, max: 100 }) // 100
|
|
31
|
+
*
|
|
32
|
+
* // Elastic overshoot (50 % blend toward max)
|
|
33
|
+
* applyConstraints(120, { min: 0, max: 100 }, 0.5) // 110
|
|
34
|
+
* ```
|
|
18
35
|
*/
|
|
19
|
-
export declare
|
|
36
|
+
export declare const applyConstraints: (point: number, range: ConstraintRange, elastic?: ConstraintElastic) => number;
|
package/dist/utils/dragMath.js
CHANGED
|
@@ -7,8 +7,23 @@ export const mixNumber = (from, to, t) => from + (to - from) * t;
|
|
|
7
7
|
* Apply float-safe constraints with optional elastic mixing.
|
|
8
8
|
* Mirrors Framer Motion behavior: clamp via Math.min/Math.max with no rounding.
|
|
9
9
|
* If `elastic` provided, blends toward the bound using its side-specific factor.
|
|
10
|
+
*
|
|
11
|
+
* @param point The unconstrained value to clamp.
|
|
12
|
+
* @param range Min/max boundaries. Either or both may be omitted.
|
|
13
|
+
* @param elastic Optional per-side elastic factor(s) in [0,1]. When provided,
|
|
14
|
+
* the value is interpolated toward the boundary instead of hard-clamped.
|
|
15
|
+
* @returns The constrained (and optionally elastically blended) value.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* // Hard clamp to [0, 100]
|
|
20
|
+
* applyConstraints(120, { min: 0, max: 100 }) // 100
|
|
21
|
+
*
|
|
22
|
+
* // Elastic overshoot (50 % blend toward max)
|
|
23
|
+
* applyConstraints(120, { min: 0, max: 100 }, 0.5) // 110
|
|
24
|
+
* ```
|
|
10
25
|
*/
|
|
11
|
-
export
|
|
26
|
+
export const applyConstraints = (point, range, elastic) => {
|
|
12
27
|
const hasMin = range.min !== undefined;
|
|
13
28
|
const hasMax = range.max !== undefined;
|
|
14
29
|
if (hasMin && point < range.min) {
|
|
@@ -26,4 +41,4 @@ export function applyConstraints(point, range, elastic) {
|
|
|
26
41
|
return Math.min(point, range.max);
|
|
27
42
|
}
|
|
28
43
|
return point;
|
|
29
|
-
}
|
|
44
|
+
};
|
|
@@ -27,10 +27,10 @@ export type BoundaryPhysics = {
|
|
|
27
27
|
* - `timeConstant` is expressed in seconds and internally converted to milliseconds.
|
|
28
28
|
* @returns {BoundaryPhysics} Fully resolved physics parameters for inertia and boundary spring.
|
|
29
29
|
*/
|
|
30
|
-
export declare
|
|
30
|
+
export declare const deriveBoundaryPhysics: (elastic: number | undefined, transition?: {
|
|
31
31
|
bounceStiffness?: number;
|
|
32
32
|
bounceDamping?: number;
|
|
33
33
|
timeConstant?: number;
|
|
34
34
|
restDelta?: number;
|
|
35
35
|
restSpeed?: number;
|
|
36
|
-
})
|
|
36
|
+
}) => BoundaryPhysics;
|
package/dist/utils/dragParams.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - `timeConstant` is expressed in seconds and internally converted to milliseconds.
|
|
14
14
|
* @returns {BoundaryPhysics} Fully resolved physics parameters for inertia and boundary spring.
|
|
15
15
|
*/
|
|
16
|
-
export
|
|
16
|
+
export const deriveBoundaryPhysics = (elastic, transition) => {
|
|
17
17
|
const truthyElastic = typeof elastic === 'number' ? elastic > 0 : !!elastic;
|
|
18
18
|
let bounceStiffness = truthyElastic ? 200 : 1_000_000;
|
|
19
19
|
let bounceDamping = truthyElastic ? 40 : 10_000_000;
|
|
@@ -26,4 +26,4 @@ export function deriveBoundaryPhysics(elastic, transition) {
|
|
|
26
26
|
const restDelta = transition?.restDelta ?? 1;
|
|
27
27
|
const restSpeed = transition?.restSpeed ?? 10;
|
|
28
28
|
return { timeConstantMs, restDelta, restSpeed, bounceStiffness, bounceDamping };
|
|
29
|
-
}
|
|
29
|
+
};
|
package/dist/utils/inertia.d.ts
CHANGED
|
@@ -7,14 +7,21 @@
|
|
|
7
7
|
*
|
|
8
8
|
* This module is SSR-safe and holds no references to the DOM or timers.
|
|
9
9
|
*/
|
|
10
|
+
/** Current position and velocity on a single axis. */
|
|
10
11
|
export type AxisState = {
|
|
11
12
|
value: number;
|
|
12
13
|
velocity: number;
|
|
13
14
|
};
|
|
15
|
+
/** Min/max boundary pair for clamping an axis. */
|
|
14
16
|
export type Bounds = {
|
|
15
17
|
min: number;
|
|
16
18
|
max: number;
|
|
17
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Parameters controlling the inertia decay and boundary spring phases.
|
|
22
|
+
*
|
|
23
|
+
* @see deriveBoundaryPhysics
|
|
24
|
+
*/
|
|
18
25
|
export type InertiaHandoffOptions = {
|
|
19
26
|
timeConstantMs: number;
|
|
20
27
|
restDelta: number;
|
|
@@ -22,6 +29,7 @@ export type InertiaHandoffOptions = {
|
|
|
22
29
|
bounceStiffness: number;
|
|
23
30
|
bounceDamping: number;
|
|
24
31
|
};
|
|
32
|
+
/** Value returned by each step of the inertia/spring simulation. */
|
|
25
33
|
export type StepResult = {
|
|
26
34
|
value: number;
|
|
27
35
|
done: boolean;
|
|
@@ -29,5 +37,20 @@ export type StepResult = {
|
|
|
29
37
|
/**
|
|
30
38
|
* Create a stepper that yields a value at each elapsed time. Handoff from
|
|
31
39
|
* inertia to spring when crossing the bounds.
|
|
40
|
+
*
|
|
41
|
+
* @param initial Starting position and velocity on the axis.
|
|
42
|
+
* @param bounds Min/max boundaries for the axis.
|
|
43
|
+
* @param opts Physics parameters for decay and boundary spring.
|
|
44
|
+
* @returns A function that accepts elapsed time in ms and returns the current `StepResult`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const step = createInertiaToBoundary(
|
|
49
|
+
* { value: 50, velocity: 200 },
|
|
50
|
+
* { min: 0, max: 300 },
|
|
51
|
+
* { timeConstantMs: 350, restDelta: 0.5, restSpeed: 10, bounceStiffness: 500, bounceDamping: 25 }
|
|
52
|
+
* )
|
|
53
|
+
* const { value, done } = step(16) // advance 16 ms
|
|
54
|
+
* ```
|
|
32
55
|
*/
|
|
33
|
-
export declare
|
|
56
|
+
export declare const createInertiaToBoundary: (initial: AxisState, bounds: Bounds, opts: InertiaHandoffOptions) => ((tMs: number) => StepResult);
|
package/dist/utils/inertia.js
CHANGED
|
@@ -51,8 +51,23 @@ const solveCrossTimeMs = (x0, v0, tauMs, boundary) => {
|
|
|
51
51
|
/**
|
|
52
52
|
* Create a stepper that yields a value at each elapsed time. Handoff from
|
|
53
53
|
* inertia to spring when crossing the bounds.
|
|
54
|
+
*
|
|
55
|
+
* @param initial Starting position and velocity on the axis.
|
|
56
|
+
* @param bounds Min/max boundaries for the axis.
|
|
57
|
+
* @param opts Physics parameters for decay and boundary spring.
|
|
58
|
+
* @returns A function that accepts elapsed time in ms and returns the current `StepResult`.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const step = createInertiaToBoundary(
|
|
63
|
+
* { value: 50, velocity: 200 },
|
|
64
|
+
* { min: 0, max: 300 },
|
|
65
|
+
* { timeConstantMs: 350, restDelta: 0.5, restSpeed: 10, bounceStiffness: 500, bounceDamping: 25 }
|
|
66
|
+
* )
|
|
67
|
+
* const { value, done } = step(16) // advance 16 ms
|
|
68
|
+
* ```
|
|
54
69
|
*/
|
|
55
|
-
export
|
|
70
|
+
export const createInertiaToBoundary = (initial, bounds, opts) => {
|
|
56
71
|
const min = bounds.min;
|
|
57
72
|
const max = bounds.max;
|
|
58
73
|
const tauMs = Math.max(1, opts.timeConstantMs);
|
|
@@ -156,4 +171,4 @@ export function createInertiaToBoundary(initial, bounds, opts) {
|
|
|
156
171
|
const settled = boundaryTarget != null ? springX : x;
|
|
157
172
|
return { value: Math.min(max, Math.max(min, settled)), done: true };
|
|
158
173
|
};
|
|
159
|
-
}
|
|
174
|
+
};
|
package/dist/utils/layoutId.d.ts
CHANGED
|
@@ -19,6 +19,15 @@ export type LayoutIdRegistry = {
|
|
|
19
19
|
export declare const layoutIdRegistry: LayoutIdRegistry;
|
|
20
20
|
/**
|
|
21
21
|
* Get the global layoutId registry.
|
|
22
|
+
*
|
|
23
|
+
* @returns The singleton `LayoutIdRegistry` instance.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const registry = getLayoutIdRegistry()
|
|
28
|
+
* registry.snapshot('hero', element.getBoundingClientRect())
|
|
29
|
+
* const entry = registry.consume('hero') // one-shot: returns and deletes
|
|
30
|
+
* ```
|
|
22
31
|
*/
|
|
23
|
-
export declare
|
|
32
|
+
export declare const getLayoutIdRegistry: () => LayoutIdRegistry;
|
|
24
33
|
export {};
|
package/dist/utils/layoutId.js
CHANGED
|
@@ -17,7 +17,16 @@ export const layoutIdRegistry = {
|
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* Get the global layoutId registry.
|
|
20
|
+
*
|
|
21
|
+
* @returns The singleton `LayoutIdRegistry` instance.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const registry = getLayoutIdRegistry()
|
|
26
|
+
* registry.snapshot('hero', element.getBoundingClientRect())
|
|
27
|
+
* const entry = registry.consume('hero') // one-shot: returns and deletes
|
|
28
|
+
* ```
|
|
20
29
|
*/
|
|
21
|
-
export
|
|
30
|
+
export const getLayoutIdRegistry = () => {
|
|
22
31
|
return layoutIdRegistry;
|
|
23
|
-
}
|
|
32
|
+
};
|
package/dist/utils/log.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect whether the current page is running inside a Playwright test.
|
|
3
|
+
*
|
|
4
|
+
* @returns `true` when the URL contains the `@isPlaywright=true` query param.
|
|
5
|
+
*/
|
|
1
6
|
export declare const isPlaywrightEnv: () => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Log to the console only in DEV mode inside a Playwright environment.
|
|
9
|
+
*
|
|
10
|
+
* @param args Values forwarded to `console.log`.
|
|
11
|
+
*/
|
|
2
12
|
export declare const pwLog: (...args: unknown[]) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Warn to the console only in DEV mode inside a Playwright environment.
|
|
15
|
+
*
|
|
16
|
+
* @param args Values forwarded to `console.warn`.
|
|
17
|
+
*/
|
|
3
18
|
export declare const pwWarn: (...args: unknown[]) => void;
|
package/dist/utils/log.js
CHANGED
|
@@ -5,11 +5,21 @@
|
|
|
5
5
|
* In production builds (npm package), these are noops to reduce bundle size.
|
|
6
6
|
*/
|
|
7
7
|
import { DEV } from 'esm-env';
|
|
8
|
+
/**
|
|
9
|
+
* Detect whether the current page is running inside a Playwright test.
|
|
10
|
+
*
|
|
11
|
+
* @returns `true` when the URL contains the `@isPlaywright=true` query param.
|
|
12
|
+
*/
|
|
8
13
|
export const isPlaywrightEnv = () => {
|
|
9
14
|
if (typeof window === 'undefined')
|
|
10
15
|
return false;
|
|
11
16
|
return window.location.search.includes('@isPlaywright=true');
|
|
12
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Log to the console only in DEV mode inside a Playwright environment.
|
|
20
|
+
*
|
|
21
|
+
* @param args Values forwarded to `console.log`.
|
|
22
|
+
*/
|
|
13
23
|
export const pwLog = (...args) => {
|
|
14
24
|
if (!DEV)
|
|
15
25
|
return;
|
|
@@ -17,6 +27,11 @@ export const pwLog = (...args) => {
|
|
|
17
27
|
return;
|
|
18
28
|
console.log(...args);
|
|
19
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Warn to the console only in DEV mode inside a Playwright environment.
|
|
32
|
+
*
|
|
33
|
+
* @param args Values forwarded to `console.warn`.
|
|
34
|
+
*/
|
|
20
35
|
export const pwWarn = (...args) => {
|
|
21
36
|
if (!DEV)
|
|
22
37
|
return;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Readable } from 'svelte/store';
|
|
2
|
+
/**
|
|
3
|
+
* Subscribes to a Svelte store and fires a callback on every *change*,
|
|
4
|
+
* skipping the initial synchronous emission that Svelte stores produce
|
|
5
|
+
* on subscribe.
|
|
6
|
+
*
|
|
7
|
+
* Returns an unsubscribe function. Use inside `$effect` or `onDestroy`
|
|
8
|
+
* for automatic cleanup.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* import { useMotionValueEvent, useSpring } from '@humanspeak/svelte-motion'
|
|
14
|
+
* import { onDestroy } from 'svelte'
|
|
15
|
+
*
|
|
16
|
+
* const x = useSpring(0)
|
|
17
|
+
* const unsub = useMotionValueEvent(x, 'change', (latest) => {
|
|
18
|
+
* console.log('x changed to', latest)
|
|
19
|
+
* })
|
|
20
|
+
* onDestroy(unsub)
|
|
21
|
+
* </script>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @param store A readable Svelte store to observe.
|
|
25
|
+
* @param event The event type — currently only `'change'` is supported.
|
|
26
|
+
* @param callback Invoked with the latest value on each change after the initial emission.
|
|
27
|
+
* @returns An unsubscribe function.
|
|
28
|
+
*/
|
|
29
|
+
export declare const useMotionValueEvent: <T>(store: Readable<T>, event: "change", callback: (latest: T) => void) => (() => void);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscribes to a Svelte store and fires a callback on every *change*,
|
|
3
|
+
* skipping the initial synchronous emission that Svelte stores produce
|
|
4
|
+
* on subscribe.
|
|
5
|
+
*
|
|
6
|
+
* Returns an unsubscribe function. Use inside `$effect` or `onDestroy`
|
|
7
|
+
* for automatic cleanup.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <script>
|
|
12
|
+
* import { useMotionValueEvent, useSpring } from '@humanspeak/svelte-motion'
|
|
13
|
+
* import { onDestroy } from 'svelte'
|
|
14
|
+
*
|
|
15
|
+
* const x = useSpring(0)
|
|
16
|
+
* const unsub = useMotionValueEvent(x, 'change', (latest) => {
|
|
17
|
+
* console.log('x changed to', latest)
|
|
18
|
+
* })
|
|
19
|
+
* onDestroy(unsub)
|
|
20
|
+
* </script>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param store A readable Svelte store to observe.
|
|
24
|
+
* @param event The event type — currently only `'change'` is supported.
|
|
25
|
+
* @param callback Invoked with the latest value on each change after the initial emission.
|
|
26
|
+
* @returns An unsubscribe function.
|
|
27
|
+
*/
|
|
28
|
+
export const useMotionValueEvent = (store, event, callback) => {
|
|
29
|
+
let initialized = false;
|
|
30
|
+
const unsub = store.subscribe((value) => {
|
|
31
|
+
if (!initialized) {
|
|
32
|
+
initialized = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
callback(value);
|
|
36
|
+
});
|
|
37
|
+
return unsub;
|
|
38
|
+
};
|
package/dist/utils/presence.d.ts
CHANGED
|
@@ -54,23 +54,23 @@ export type AnimatePresenceContext = {
|
|
|
54
54
|
* @param context Optional callbacks, for example `onExitComplete`.
|
|
55
55
|
* @returns A presence context with register/update/unregister APIs.
|
|
56
56
|
*/
|
|
57
|
-
export declare
|
|
57
|
+
export declare const createAnimatePresenceContext: (context: {
|
|
58
58
|
initial?: boolean;
|
|
59
59
|
mode?: AnimatePresenceMode;
|
|
60
60
|
onExitComplete?: () => void;
|
|
61
|
-
})
|
|
61
|
+
}) => AnimatePresenceContext;
|
|
62
62
|
/**
|
|
63
63
|
* Get the current `AnimatePresence` context from Svelte component context.
|
|
64
64
|
*
|
|
65
65
|
* Note: Trivial wrapper - ignored for coverage.
|
|
66
66
|
*/
|
|
67
|
-
export declare
|
|
67
|
+
export declare const getAnimatePresenceContext: () => AnimatePresenceContext | undefined;
|
|
68
68
|
/**
|
|
69
69
|
* Set the `AnimatePresence` context into Svelte component context.
|
|
70
70
|
*
|
|
71
71
|
* Note: Trivial wrapper - ignored for coverage.
|
|
72
72
|
*/
|
|
73
|
-
export declare
|
|
73
|
+
export declare const setAnimatePresenceContext: (context: AnimatePresenceContext) => void;
|
|
74
74
|
/**
|
|
75
75
|
* Get the current presence depth from Svelte component context.
|
|
76
76
|
*
|
|
@@ -127,4 +127,4 @@ export declare const setPresenceDepth: (depth: number) => void;
|
|
|
127
127
|
* @param exit The exit keyframes definition.
|
|
128
128
|
* @param mergedTransition The element's merged transition for precedence.
|
|
129
129
|
*/
|
|
130
|
-
export declare
|
|
130
|
+
export declare const usePresence: (key: string, element: HTMLElement | null, exit: MotionExit, mergedTransition?: MotionTransition) => void;
|
package/dist/utils/presence.js
CHANGED
|
@@ -51,7 +51,7 @@ const resetTransforms = (element) => {
|
|
|
51
51
|
* @param context Optional callbacks, for example `onExitComplete`.
|
|
52
52
|
* @returns A presence context with register/update/unregister APIs.
|
|
53
53
|
*/
|
|
54
|
-
export
|
|
54
|
+
export const createAnimatePresenceContext = (context) => {
|
|
55
55
|
// Default initial to true (animate on first mount) unless explicitly false
|
|
56
56
|
const initial = context.initial !== false;
|
|
57
57
|
// Default mode to 'sync' if not specified
|
|
@@ -466,25 +466,25 @@ export function createAnimatePresenceContext(context) {
|
|
|
466
466
|
updateChildState,
|
|
467
467
|
unregisterChild
|
|
468
468
|
};
|
|
469
|
-
}
|
|
469
|
+
};
|
|
470
470
|
/**
|
|
471
471
|
* Get the current `AnimatePresence` context from Svelte component context.
|
|
472
472
|
*
|
|
473
473
|
* Note: Trivial wrapper - ignored for coverage.
|
|
474
474
|
*/
|
|
475
475
|
/* c8 ignore next 3 */
|
|
476
|
-
export
|
|
476
|
+
export const getAnimatePresenceContext = () => {
|
|
477
477
|
return getContext(ANIMATE_PRESENCE_CONTEXT);
|
|
478
|
-
}
|
|
478
|
+
};
|
|
479
479
|
/**
|
|
480
480
|
* Set the `AnimatePresence` context into Svelte component context.
|
|
481
481
|
*
|
|
482
482
|
* Note: Trivial wrapper - ignored for coverage.
|
|
483
483
|
*/
|
|
484
484
|
/* c8 ignore next 3 */
|
|
485
|
-
export
|
|
485
|
+
export const setAnimatePresenceContext = (context) => {
|
|
486
486
|
setContext(ANIMATE_PRESENCE_CONTEXT, context);
|
|
487
|
-
}
|
|
487
|
+
};
|
|
488
488
|
/**
|
|
489
489
|
* Get the current presence depth from Svelte component context.
|
|
490
490
|
*
|
|
@@ -546,7 +546,7 @@ export const setPresenceDepth = (depth) => {
|
|
|
546
546
|
* @param exit The exit keyframes definition.
|
|
547
547
|
* @param mergedTransition The element's merged transition for precedence.
|
|
548
548
|
*/
|
|
549
|
-
export
|
|
549
|
+
export const usePresence = (key, element, exit, mergedTransition) => {
|
|
550
550
|
const context = getAnimatePresenceContext();
|
|
551
551
|
pwLog('[presence] usePresence called', {
|
|
552
552
|
key,
|
|
@@ -567,5 +567,5 @@ export function usePresence(key, element, exit, mergedTransition) {
|
|
|
567
567
|
reason: !element ? 'no element' : !context ? 'no context' : 'no exit'
|
|
568
568
|
});
|
|
569
569
|
}
|
|
570
|
-
}
|
|
570
|
+
};
|
|
571
571
|
/* c8 ignore end */
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
/**
|
|
3
|
+
* A scroll offset edge defined as a string (e.g. `"start"`, `"end"`, `"center"`)
|
|
4
|
+
* or a number (0–1 progress). Each offset entry is a pair of `[target, container]`.
|
|
5
|
+
*/
|
|
6
|
+
type ScrollOffset = Array<[number | string, number | string]> | string[];
|
|
7
|
+
/**
|
|
8
|
+
* An element reference — either an element directly or a getter function
|
|
9
|
+
* that returns one (useful with Svelte's `bind:this` where the element
|
|
10
|
+
* isn't available until after mount).
|
|
11
|
+
*/
|
|
12
|
+
type ElementOrGetter = HTMLElement | (() => HTMLElement | undefined);
|
|
13
|
+
/**
|
|
14
|
+
* Options accepted by `useScroll`.
|
|
15
|
+
*/
|
|
16
|
+
type UseScrollOptions = {
|
|
17
|
+
/** Scrollable container to track. Defaults to the page. Accepts an element or a getter function. */
|
|
18
|
+
container?: ElementOrGetter;
|
|
19
|
+
/** Target element to track position of within the container. Accepts an element or a getter function. */
|
|
20
|
+
target?: ElementOrGetter;
|
|
21
|
+
/** Scroll offset configuration for element position tracking. */
|
|
22
|
+
offset?: ScrollOffset;
|
|
23
|
+
/** Which axis to use for the single-axis `progress` value supplied to `scroll()`. */
|
|
24
|
+
axis?: 'x' | 'y';
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Return type of `useScroll` — four readable Svelte stores representing
|
|
28
|
+
* scroll position and normalised progress for both axes.
|
|
29
|
+
*/
|
|
30
|
+
type UseScrollReturn = {
|
|
31
|
+
scrollX: Readable<number>;
|
|
32
|
+
scrollY: Readable<number>;
|
|
33
|
+
scrollXProgress: Readable<number>;
|
|
34
|
+
scrollYProgress: Readable<number>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Creates scroll-linked Svelte stores for building scroll-driven animations
|
|
38
|
+
* such as progress indicators and parallax effects.
|
|
39
|
+
*
|
|
40
|
+
* When the returned stores are used with `opacity` / `transform` CSS properties
|
|
41
|
+
* the animations can be hardware accelerated by the browser.
|
|
42
|
+
*
|
|
43
|
+
* SSR-safe: returns static `readable(0)` stores on the server.
|
|
44
|
+
*
|
|
45
|
+
* `container` and `target` accept either an `HTMLElement` directly or a
|
|
46
|
+
* getter function `() => HTMLElement | undefined`. This is useful with
|
|
47
|
+
* Svelte's `bind:this` where the element isn't available until after mount.
|
|
48
|
+
* When a getter is provided, element resolution is deferred until the
|
|
49
|
+
* first subscriber arrives, and if the element isn't available yet the
|
|
50
|
+
* stores poll on each animation frame until it is.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```svelte
|
|
54
|
+
* <script>
|
|
55
|
+
* import { useScroll, useSpring } from '@humanspeak/svelte-motion'
|
|
56
|
+
*
|
|
57
|
+
* const { scrollYProgress } = useScroll()
|
|
58
|
+
* const scaleX = useSpring(scrollYProgress)
|
|
59
|
+
* </script>
|
|
60
|
+
*
|
|
61
|
+
* <div style="transform: scaleX({$scaleX}); transform-origin: left;" />
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @param options Optional scroll tracking configuration.
|
|
65
|
+
* @returns An object with `scrollX`, `scrollY`, `scrollXProgress`, and `scrollYProgress` stores.
|
|
66
|
+
*/
|
|
67
|
+
export declare const useScroll: (options?: UseScrollOptions) => UseScrollReturn;
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { scroll } from 'motion';
|
|
2
|
+
import { readable, writable } from 'svelte/store';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves an element-or-getter to an HTMLElement (or undefined).
|
|
5
|
+
*/
|
|
6
|
+
const resolveElement = (ref) => {
|
|
7
|
+
if (!ref)
|
|
8
|
+
return undefined;
|
|
9
|
+
return typeof ref === 'function' ? ref() : ref;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Creates scroll-linked Svelte stores for building scroll-driven animations
|
|
13
|
+
* such as progress indicators and parallax effects.
|
|
14
|
+
*
|
|
15
|
+
* When the returned stores are used with `opacity` / `transform` CSS properties
|
|
16
|
+
* the animations can be hardware accelerated by the browser.
|
|
17
|
+
*
|
|
18
|
+
* SSR-safe: returns static `readable(0)` stores on the server.
|
|
19
|
+
*
|
|
20
|
+
* `container` and `target` accept either an `HTMLElement` directly or a
|
|
21
|
+
* getter function `() => HTMLElement | undefined`. This is useful with
|
|
22
|
+
* Svelte's `bind:this` where the element isn't available until after mount.
|
|
23
|
+
* When a getter is provided, element resolution is deferred until the
|
|
24
|
+
* first subscriber arrives, and if the element isn't available yet the
|
|
25
|
+
* stores poll on each animation frame until it is.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```svelte
|
|
29
|
+
* <script>
|
|
30
|
+
* import { useScroll, useSpring } from '@humanspeak/svelte-motion'
|
|
31
|
+
*
|
|
32
|
+
* const { scrollYProgress } = useScroll()
|
|
33
|
+
* const scaleX = useSpring(scrollYProgress)
|
|
34
|
+
* </script>
|
|
35
|
+
*
|
|
36
|
+
* <div style="transform: scaleX({$scaleX}); transform-origin: left;" />
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param options Optional scroll tracking configuration.
|
|
40
|
+
* @returns An object with `scrollX`, `scrollY`, `scrollXProgress`, and `scrollYProgress` stores.
|
|
41
|
+
*/
|
|
42
|
+
export const useScroll = (options) => {
|
|
43
|
+
if (typeof window === 'undefined') {
|
|
44
|
+
return {
|
|
45
|
+
scrollX: readable(0),
|
|
46
|
+
scrollY: readable(0),
|
|
47
|
+
scrollXProgress: readable(0),
|
|
48
|
+
scrollYProgress: readable(0)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const stores = {
|
|
52
|
+
scrollX: writable(0),
|
|
53
|
+
scrollY: writable(0),
|
|
54
|
+
scrollXProgress: writable(0),
|
|
55
|
+
scrollYProgress: writable(0)
|
|
56
|
+
};
|
|
57
|
+
let cleanup;
|
|
58
|
+
let pollRaf = 0;
|
|
59
|
+
let subscriberCount = 0;
|
|
60
|
+
const attach = () => {
|
|
61
|
+
if (cleanup)
|
|
62
|
+
return;
|
|
63
|
+
// Resolve elements — they may not be available yet when using getters
|
|
64
|
+
const container = resolveElement(options?.container);
|
|
65
|
+
const target = resolveElement(options?.target);
|
|
66
|
+
// If a getter was provided but returned undefined, the element isn't
|
|
67
|
+
// mounted yet. Poll on the next frame until it appears.
|
|
68
|
+
const needsContainer = options?.container && !container;
|
|
69
|
+
const needsTarget = options?.target && !target;
|
|
70
|
+
if (needsContainer || needsTarget) {
|
|
71
|
+
if (!pollRaf) {
|
|
72
|
+
pollRaf = requestAnimationFrame(() => {
|
|
73
|
+
pollRaf = 0;
|
|
74
|
+
attach();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
cleanup = scroll((_progress, info) => {
|
|
80
|
+
stores.scrollX.set(info.x.current);
|
|
81
|
+
stores.scrollY.set(info.y.current);
|
|
82
|
+
stores.scrollXProgress.set(info.x.progress);
|
|
83
|
+
stores.scrollYProgress.set(info.y.progress);
|
|
84
|
+
}, {
|
|
85
|
+
container,
|
|
86
|
+
target,
|
|
87
|
+
offset: options?.offset,
|
|
88
|
+
axis: options?.axis
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const detach = () => {
|
|
92
|
+
if (subscriberCount <= 0) {
|
|
93
|
+
if (pollRaf) {
|
|
94
|
+
cancelAnimationFrame(pollRaf);
|
|
95
|
+
pollRaf = 0;
|
|
96
|
+
}
|
|
97
|
+
if (cleanup) {
|
|
98
|
+
cleanup();
|
|
99
|
+
cleanup = undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const make = (key) => readable(0, (set) => {
|
|
104
|
+
subscriberCount++;
|
|
105
|
+
const unsub = stores[key].subscribe(set);
|
|
106
|
+
attach();
|
|
107
|
+
return () => {
|
|
108
|
+
unsub();
|
|
109
|
+
subscriberCount--;
|
|
110
|
+
detach();
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
scrollX: make('scrollX'),
|
|
115
|
+
scrollY: make('scrollY'),
|
|
116
|
+
scrollXProgress: make('scrollXProgress'),
|
|
117
|
+
scrollYProgress: make('scrollYProgress')
|
|
118
|
+
};
|
|
119
|
+
};
|
package/dist/utils/testing.d.ts
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return a promise that resolves after the given number of milliseconds.
|
|
3
|
+
*
|
|
4
|
+
* @param ms Delay in milliseconds.
|
|
5
|
+
* @returns A promise that resolves after the delay.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* await sleep(100) // pause for 100 ms
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
1
12
|
export declare const sleep: (ms: number) => Promise<unknown>;
|
package/dist/utils/testing.js
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return a promise that resolves after the given number of milliseconds.
|
|
3
|
+
*
|
|
4
|
+
* @param ms Delay in milliseconds.
|
|
5
|
+
* @returns A promise that resolves after the delay.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* await sleep(100) // pause for 100 ms
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
1
12
|
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -6,13 +6,30 @@ export type TransformValues = Partial<{
|
|
|
6
6
|
scaleY: number;
|
|
7
7
|
rotate: number;
|
|
8
8
|
}>;
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Build a CSS transform string from numeric values (no matrices).
|
|
11
|
+
*
|
|
12
|
+
* @param values Partial map of translate/scale/rotate values.
|
|
13
|
+
* @returns A space-separated CSS `transform` string, or `""` when all values are defaults.
|
|
14
|
+
*/
|
|
15
|
+
export declare const buildTransform: (values: TransformValues) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Lightweight safety check for transform magnitudes and NaN values.
|
|
18
|
+
*
|
|
19
|
+
* @param values Transform values to validate.
|
|
20
|
+
* @param opts Optional configuration; `maxScale` caps allowable absolute scale (default 8).
|
|
21
|
+
* @returns `true` if all scale values are finite and within bounds.
|
|
22
|
+
*/
|
|
23
|
+
export declare const isSafeTransform: (values: TransformValues, opts?: {
|
|
13
24
|
maxScale?: number;
|
|
14
|
-
})
|
|
15
|
-
|
|
25
|
+
}) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Extract the uniform scale factor from a CSS `matrix()` string.
|
|
28
|
+
*
|
|
29
|
+
* @param matrix A CSS `matrix(...)` value, `"none"`, `null`, or `undefined`.
|
|
30
|
+
* @returns The `a` component of the matrix (uniform scale), or `null` if unparseable.
|
|
31
|
+
*/
|
|
32
|
+
export declare const parseMatrixScale: (matrix: string | null | undefined) => number | null;
|
|
16
33
|
import { type Readable } from 'svelte/store';
|
|
17
34
|
/**
|
|
18
35
|
* Options for range-mapping transform.
|
package/dist/utils/transform.js
CHANGED
|
@@ -7,8 +7,13 @@ const DEFAULTS = {
|
|
|
7
7
|
scaleY: 1,
|
|
8
8
|
rotate: 0
|
|
9
9
|
};
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Build a CSS transform string from numeric values (no matrices).
|
|
12
|
+
*
|
|
13
|
+
* @param values Partial map of translate/scale/rotate values.
|
|
14
|
+
* @returns A space-separated CSS `transform` string, or `""` when all values are defaults.
|
|
15
|
+
*/
|
|
16
|
+
export const buildTransform = (values) => {
|
|
12
17
|
const v = { ...DEFAULTS, ...values };
|
|
13
18
|
// If explicit per-axis scales provided, use them; otherwise use uniform scale
|
|
14
19
|
const useAxes = values.scaleX !== undefined || values.scaleY !== undefined;
|
|
@@ -26,9 +31,15 @@ export function buildTransform(values) {
|
|
|
26
31
|
parts.push(`scale(${round(v.scale)})`);
|
|
27
32
|
}
|
|
28
33
|
return parts.join(' ').trim();
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Lightweight safety check for transform magnitudes and NaN values.
|
|
37
|
+
*
|
|
38
|
+
* @param values Transform values to validate.
|
|
39
|
+
* @param opts Optional configuration; `maxScale` caps allowable absolute scale (default 8).
|
|
40
|
+
* @returns `true` if all scale values are finite and within bounds.
|
|
41
|
+
*/
|
|
42
|
+
export const isSafeTransform = (values, opts) => {
|
|
32
43
|
const maxScale = opts?.maxScale ?? 8;
|
|
33
44
|
const entries = [
|
|
34
45
|
['scale', values.scale],
|
|
@@ -44,8 +55,14 @@ export function isSafeTransform(values, opts) {
|
|
|
44
55
|
return false;
|
|
45
56
|
}
|
|
46
57
|
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Extract the uniform scale factor from a CSS `matrix()` string.
|
|
61
|
+
*
|
|
62
|
+
* @param matrix A CSS `matrix(...)` value, `"none"`, `null`, or `undefined`.
|
|
63
|
+
* @returns The `a` component of the matrix (uniform scale), or `null` if unparseable.
|
|
64
|
+
*/
|
|
65
|
+
export const parseMatrixScale = (matrix) => {
|
|
49
66
|
if (!matrix || matrix === 'none')
|
|
50
67
|
return null;
|
|
51
68
|
const m = matrix.match(/matrix\(([^)]+)\)/);
|
|
@@ -53,11 +70,16 @@ export function parseMatrixScale(matrix) {
|
|
|
53
70
|
return null;
|
|
54
71
|
const [a] = m[1].split(',').map((s) => parseFloat(s.trim()));
|
|
55
72
|
return Number.isFinite(a) ? a : null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Round a number to six decimal places to avoid excessive precision in CSS strings.
|
|
76
|
+
*
|
|
77
|
+
* @param n The number to round.
|
|
78
|
+
* @returns The rounded value.
|
|
79
|
+
*/
|
|
80
|
+
const round = (n) => {
|
|
59
81
|
return Math.round(n * 1e6) / 1e6;
|
|
60
|
-
}
|
|
82
|
+
};
|
|
61
83
|
import { derived, readable } from 'svelte/store';
|
|
62
84
|
/**
|
|
63
85
|
* Creates a linear mixer function for numeric values.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
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",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"motion": "^12.34.
|
|
57
|
-
"motion-dom": "^12.34.
|
|
56
|
+
"motion": "^12.34.3",
|
|
57
|
+
"motion-dom": "^12.34.3"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@changesets/cli": "^2.29.8",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@eslint/js": "^10.0.1",
|
|
63
63
|
"@playwright/test": "^1.58.2",
|
|
64
64
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
65
|
-
"@sveltejs/kit": "^2.
|
|
65
|
+
"@sveltejs/kit": "^2.53.0",
|
|
66
66
|
"@sveltejs/package": "^2.5.7",
|
|
67
67
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
68
68
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@types/node": "^25.3.0",
|
|
76
76
|
"@vitest/coverage-v8": "^4.0.18",
|
|
77
77
|
"concurrently": "^9.2.1",
|
|
78
|
-
"eslint": "^10.0.
|
|
78
|
+
"eslint": "^10.0.1",
|
|
79
79
|
"eslint-config-prettier": "10.1.8",
|
|
80
80
|
"eslint-plugin-import": "2.32.0",
|
|
81
81
|
"eslint-plugin-svelte": "3.15.0",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
94
94
|
"publint": "^0.3.17",
|
|
95
95
|
"runed": "0.37.1",
|
|
96
|
-
"svelte": "^5.53.
|
|
97
|
-
"svelte-check": "^4.4.
|
|
96
|
+
"svelte": "^5.53.2",
|
|
97
|
+
"svelte-check": "^4.4.3",
|
|
98
98
|
"svg-tags": "^1.0.0",
|
|
99
99
|
"tailwind-merge": "^3.5.0",
|
|
100
100
|
"tailwind-variants": "^3.2.2",
|