@humanspeak/svelte-motion 0.4.1 → 0.4.3
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 +3 -1
- package/dist/types.d.ts +25 -0
- package/dist/utils/inView.d.ts +5 -2
- package/dist/utils/inView.js +24 -4
- package/package.json +5 -5
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
whileHover: whileHoverProp,
|
|
88
88
|
whileFocus: whileFocusProp,
|
|
89
89
|
whileInView: whileInViewProp,
|
|
90
|
+
viewport: viewportProp,
|
|
90
91
|
whileDrag: whileDragProp,
|
|
91
92
|
onHoverStart: onHoverStartProp,
|
|
92
93
|
onHoverEnd: onHoverEndProp,
|
|
@@ -781,7 +782,8 @@
|
|
|
781
782
|
{
|
|
782
783
|
initial: (resolvedInitial ?? {}) as Record<string, unknown>,
|
|
783
784
|
animate: (resolvedAnimate ?? {}) as Record<string, unknown>
|
|
784
|
-
}
|
|
785
|
+
},
|
|
786
|
+
viewportProp
|
|
785
787
|
)
|
|
786
788
|
})
|
|
787
789
|
|
package/dist/types.d.ts
CHANGED
|
@@ -133,6 +133,29 @@ export type MotionWhileDrag = (Record<string, unknown> & {
|
|
|
133
133
|
export type MotionWhileInView = (Record<string, unknown> & {
|
|
134
134
|
transition?: AnimationOptions;
|
|
135
135
|
}) | DOMKeyframesDefinition | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* IntersectionObserver configuration for `whileInView`. Mirrors framer-motion's
|
|
138
|
+
* `viewport` prop. Same shape as `UseInViewOptions` minus `initial` (which is
|
|
139
|
+
* only meaningful for the hook's pre-mount return value).
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```svelte
|
|
143
|
+
* <motion.div
|
|
144
|
+
* whileInView={{ opacity: 1, y: 0 }}
|
|
145
|
+
* viewport={{ once: true, amount: 0.5 }}
|
|
146
|
+
* />
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export type MotionViewport = {
|
|
150
|
+
/** When `true`, fire only once on first entry. Subsequent re-entries no-op. */
|
|
151
|
+
once?: boolean;
|
|
152
|
+
/** Element to use as the IntersectionObserver root. Defaults to the viewport. */
|
|
153
|
+
root?: Element | Document;
|
|
154
|
+
/** CSS margin string applied to the root bounding box (e.g. `"100px 0px"`). */
|
|
155
|
+
margin?: string;
|
|
156
|
+
/** Fraction (0-1) or `"some"` / `"all"` of the target that must be visible. */
|
|
157
|
+
amount?: 'some' | 'all' | number;
|
|
158
|
+
};
|
|
136
159
|
/**
|
|
137
160
|
* Animation transition configuration for hover interactions.
|
|
138
161
|
* Overrides the global transition when provided.
|
|
@@ -240,6 +263,8 @@ export type MotionProps = {
|
|
|
240
263
|
whileDrag?: MotionWhileDrag;
|
|
241
264
|
/** In-view interaction animation - animates when element enters viewport */
|
|
242
265
|
whileInView?: MotionWhileInView;
|
|
266
|
+
/** IntersectionObserver options for `whileInView` (once / root / margin / amount) */
|
|
267
|
+
viewport?: MotionViewport;
|
|
243
268
|
/** Called right before a main animate transition starts */
|
|
244
269
|
onAnimationStart?: MotionAnimationStart;
|
|
245
270
|
/** Called after a main animate transition completes */
|
package/dist/utils/inView.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AnimationOptions, type DOMKeyframesDefinition } from 'motion';
|
|
2
2
|
import { type Readable } from 'svelte/store';
|
|
3
|
+
import type { MotionViewport } from '../types.js';
|
|
3
4
|
import { type ElementOrGetter } from './dom.js';
|
|
4
5
|
/**
|
|
5
6
|
* Split a whileInView definition into keyframes and an optional nested transition.
|
|
@@ -57,6 +58,7 @@ export declare const computeInViewBaseline: (el: HTMLElement, opts: {
|
|
|
57
58
|
* @param mergedTransition Root/component merged transition.
|
|
58
59
|
* @param callbacks Optional lifecycle callbacks for in-view start/end and animation completion.
|
|
59
60
|
* @param baselineSources Optional sources used to compute baseline.
|
|
61
|
+
* @param viewport Optional IntersectionObserver options. `amount` defaults to `0` (any pixel visible).
|
|
60
62
|
* @returns Cleanup function to stop observing.
|
|
61
63
|
* @example
|
|
62
64
|
* const cleanup = attachWhileInView(
|
|
@@ -67,7 +69,8 @@ export declare const computeInViewBaseline: (el: HTMLElement, opts: {
|
|
|
67
69
|
* onStart: () => console.log('Entered viewport'),
|
|
68
70
|
* onEnd: () => console.log('Left viewport')
|
|
69
71
|
* },
|
|
70
|
-
* { initial: { opacity: 0, y: 50 } }
|
|
72
|
+
* { initial: { opacity: 0, y: 50 } },
|
|
73
|
+
* { once: true, amount: 0.5 }
|
|
71
74
|
* )
|
|
72
75
|
* // Later: cleanup() to stop observing
|
|
73
76
|
*/
|
|
@@ -78,7 +81,7 @@ export declare const attachWhileInView: (el: HTMLElement, whileInView: Record<st
|
|
|
78
81
|
}, baselineSources?: {
|
|
79
82
|
initial?: Record<string, unknown>;
|
|
80
83
|
animate?: Record<string, unknown>;
|
|
81
|
-
}) => (() => void);
|
|
84
|
+
}, viewport?: MotionViewport) => (() => void);
|
|
82
85
|
/**
|
|
83
86
|
* Options accepted by `useInView`.
|
|
84
87
|
*/
|
package/dist/utils/inView.js
CHANGED
|
@@ -122,6 +122,7 @@ export const computeInViewBaseline = (el, opts) => {
|
|
|
122
122
|
* @param mergedTransition Root/component merged transition.
|
|
123
123
|
* @param callbacks Optional lifecycle callbacks for in-view start/end and animation completion.
|
|
124
124
|
* @param baselineSources Optional sources used to compute baseline.
|
|
125
|
+
* @param viewport Optional IntersectionObserver options. `amount` defaults to `0` (any pixel visible).
|
|
125
126
|
* @returns Cleanup function to stop observing.
|
|
126
127
|
* @example
|
|
127
128
|
* const cleanup = attachWhileInView(
|
|
@@ -132,14 +133,18 @@ export const computeInViewBaseline = (el, opts) => {
|
|
|
132
133
|
* onStart: () => console.log('Entered viewport'),
|
|
133
134
|
* onEnd: () => console.log('Left viewport')
|
|
134
135
|
* },
|
|
135
|
-
* { initial: { opacity: 0, y: 50 } }
|
|
136
|
+
* { initial: { opacity: 0, y: 50 } },
|
|
137
|
+
* { once: true, amount: 0.5 }
|
|
136
138
|
* )
|
|
137
139
|
* // Later: cleanup() to stop observing
|
|
138
140
|
*/
|
|
139
|
-
export const attachWhileInView = (el, whileInView, mergedTransition, callbacks, baselineSources) => {
|
|
141
|
+
export const attachWhileInView = (el, whileInView, mergedTransition, callbacks, baselineSources, viewport) => {
|
|
140
142
|
if (!whileInView)
|
|
141
143
|
return () => { };
|
|
142
|
-
|
|
144
|
+
let latched = false;
|
|
145
|
+
const stop = motionInView(el, () => {
|
|
146
|
+
if (latched)
|
|
147
|
+
return;
|
|
143
148
|
const inViewBaseline = computeInViewBaseline(el, {
|
|
144
149
|
initial: baselineSources?.initial,
|
|
145
150
|
animate: baselineSources?.animate,
|
|
@@ -155,13 +160,28 @@ export const attachWhileInView = (el, whileInView, mergedTransition, callbacks,
|
|
|
155
160
|
.catch(() => {
|
|
156
161
|
/* animation cancelled — skip completion callback */
|
|
157
162
|
});
|
|
163
|
+
if (viewport?.once) {
|
|
164
|
+
// Latch on first entry. Don't return an exit handler so the
|
|
165
|
+
// element holds its in-view state and we never animate back.
|
|
166
|
+
// Stop observing entirely after the entry handler returns.
|
|
167
|
+
latched = true;
|
|
168
|
+
queueMicrotask(stop);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
158
171
|
return () => {
|
|
159
172
|
if (Object.keys(inViewBaseline).length > 0) {
|
|
160
173
|
animate(el, inViewBaseline, mergedTransition);
|
|
161
174
|
}
|
|
162
175
|
callbacks?.onEnd?.();
|
|
163
176
|
};
|
|
164
|
-
}, {
|
|
177
|
+
}, {
|
|
178
|
+
root: viewport?.root,
|
|
179
|
+
// framer-motion types `margin` as a CSS-shorthand template literal;
|
|
180
|
+
// we expose plain `string` so consumers can pass any computed value.
|
|
181
|
+
margin: viewport?.margin,
|
|
182
|
+
amount: viewport?.amount ?? 0
|
|
183
|
+
});
|
|
184
|
+
return stop;
|
|
165
185
|
};
|
|
166
186
|
/**
|
|
167
187
|
* Returns a Svelte readable store that tracks whether `target` is in the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
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.39.0",
|
|
99
|
+
"motion-dom": "^12.39.0"
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
102
|
"@changesets/cli": "^2.31.0",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"@tailwindcss/vite": "^4.3.0",
|
|
115
115
|
"@testing-library/jest-dom": "^6.9.1",
|
|
116
116
|
"@testing-library/svelte": "^5.3.1",
|
|
117
|
-
"@types/node": "^25.
|
|
117
|
+
"@types/node": "^25.9.0",
|
|
118
118
|
"@vitest/coverage-v8": "^4.1.6",
|
|
119
119
|
"eslint": "^10.4.0",
|
|
120
120
|
"eslint-config-prettier": "10.1.8",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"tailwind-variants": "^3.2.2",
|
|
143
143
|
"tailwindcss": "^4.3.0",
|
|
144
144
|
"tailwindcss-animate": "^1.0.7",
|
|
145
|
-
"tsx": "^4.22.
|
|
145
|
+
"tsx": "^4.22.2",
|
|
146
146
|
"typescript": "^6.0.3",
|
|
147
147
|
"typescript-eslint": "^8.59.3",
|
|
148
148
|
"vite": "^8.0.13",
|