solidjs-motion 0.1.0 → 0.1.2
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/CHANGELOG.md +35 -0
- package/README.md +291 -0
- package/dist/index.js +105 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/primitives/createDrag.ts +125 -4
- package/src/primitives/gesture-state.ts +16 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ All notable changes to `solidjs-motion` / `@solidjs-motion/motion` are documente
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.2] — 2026-05-20
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Drag now coexists with `initial` / `animate` / `exit`.** A draggable
|
|
13
|
+
element with an entrance animation (`<motion.X drag initial={{x:-300}}
|
|
14
|
+
animate={{x:0}}>`) used to break in two ways: animate's `x`/`y` never
|
|
15
|
+
reached the DOM, and on pointerdown the element snapped back to the
|
|
16
|
+
initial position. Three linked fixes:
|
|
17
|
+
- Drag's exclusion of `x`/`y` from the gesture-state winners is now
|
|
18
|
+
gated on `active.whileDrag` (pointer-engaged) instead of
|
|
19
|
+
`dragEnabled` (configured), matching motion-react. Animate's `x`/`y`
|
|
20
|
+
flow normally while drag is configured but idle; drag only claims
|
|
21
|
+
them during active interaction.
|
|
22
|
+
- The diff effect's removed-key fallback no longer reverts `x`/`y` to
|
|
23
|
+
`initial` when drag activates and excludes them. Drag *claiming* a
|
|
24
|
+
key is not the same as removing it.
|
|
25
|
+
- `handlePanStart` now syncs the x/y MotionValues to the element's
|
|
26
|
+
current visible translate (parsed from `getComputedStyle` +
|
|
27
|
+
`el.style.transform`) before capturing `dragStart`. motion's
|
|
28
|
+
`animate(el, target)` interpolates style.transform via WAAPI but
|
|
29
|
+
doesn't keep the visualElement's MVs in sync, so the MV would still
|
|
30
|
+
hold the entrance start value when drag began.
|
|
31
|
+
|
|
32
|
+
## [0.1.1] — 2026-05-20
|
|
33
|
+
|
|
34
|
+
### Docs
|
|
35
|
+
|
|
36
|
+
- Expanded README with a 12-recipe "Recipes" section covering the full v0.1
|
|
37
|
+
surface: reactive `useMotion`, `<motion.X>` proxy + variant cascade,
|
|
38
|
+
`motion.create` HOC, MotionValues + `createTransform`/`createSpring`,
|
|
39
|
+
`createScroll`/`createInView`/`createTemplate`, `<Presence>` (single +
|
|
40
|
+
list + `mode="wait"`), drag with constraints, `<MotionConfig>` +
|
|
41
|
+
`createReducedMotion`.
|
|
42
|
+
|
|
8
43
|
## [0.1.0] — 2026-05-20
|
|
9
44
|
|
|
10
45
|
First public release. Five phases of the port plan land together: the canonical
|
package/README.md
CHANGED
|
@@ -46,6 +46,297 @@ export function Card() {
|
|
|
46
46
|
with motion's initial styles; user refs and motion's ref both fire; the initial style is
|
|
47
47
|
serialized into SSR HTML so the first paint is flicker-free.
|
|
48
48
|
|
|
49
|
+
## Recipes
|
|
50
|
+
|
|
51
|
+
### 1. Reactive options
|
|
52
|
+
|
|
53
|
+
Pass a function to `useMotion` to track Solid signals inside the target.
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { useMotion } from "solidjs-motion"
|
|
57
|
+
import { createSignal } from "solid-js"
|
|
58
|
+
|
|
59
|
+
export function Toggle() {
|
|
60
|
+
const [open, setOpen] = createSignal(false)
|
|
61
|
+
const motion = useMotion(() => ({
|
|
62
|
+
animate: { rotate: open() ? 180 : 0 },
|
|
63
|
+
transition: { duration: 0.3 },
|
|
64
|
+
}))
|
|
65
|
+
return (
|
|
66
|
+
<button onClick={() => setOpen((p) => !p)} {...motion()}>
|
|
67
|
+
↑
|
|
68
|
+
</button>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. `<motion.X>` proxy with variants
|
|
74
|
+
|
|
75
|
+
Every HTML/SVG tag is reachable off `motion`. Variant labels on the parent cascade to
|
|
76
|
+
descendants through `m.Provider` (auto-installed by the proxy).
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { motion } from "solidjs-motion"
|
|
80
|
+
|
|
81
|
+
const variants = {
|
|
82
|
+
rest: { y: 0, scale: 1 },
|
|
83
|
+
lift: { y: -8, scale: 1.04 },
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function Card() {
|
|
87
|
+
return (
|
|
88
|
+
<motion.article animate="rest" hover="lift" variants={variants}>
|
|
89
|
+
<motion.h2 variants={variants}>Inherits lift on hover</motion.h2>
|
|
90
|
+
</motion.article>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. `motion.create(Component)` HOC
|
|
96
|
+
|
|
97
|
+
Wrap a custom component to make it motion-aware. The wrapped component must spread its
|
|
98
|
+
props (including `ref`) onto a single DOM-element root.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { motion } from "solidjs-motion"
|
|
102
|
+
import type { ComponentProps } from "solid-js"
|
|
103
|
+
|
|
104
|
+
function Button(props: ComponentProps<"button">) {
|
|
105
|
+
return <button {...props} class={`btn ${props.class ?? ""}`} />
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const MotionButton = motion.create(Button)
|
|
109
|
+
|
|
110
|
+
export function Stage() {
|
|
111
|
+
return (
|
|
112
|
+
<MotionButton hover={{ scale: 1.05 }} press={{ scale: 0.95 }}>
|
|
113
|
+
Press me
|
|
114
|
+
</MotionButton>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4. MotionValues + `createTransform`
|
|
120
|
+
|
|
121
|
+
MotionValues are the source of truth for animated state. They're both Solid Accessors and
|
|
122
|
+
upstream `MotionValue`s — drop them straight into `style`.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { motion, createMotionValue, createTransform } from "solidjs-motion"
|
|
126
|
+
|
|
127
|
+
export function FadeSlider() {
|
|
128
|
+
const x = createMotionValue(0)
|
|
129
|
+
const opacity = createTransform(x, [-100, 0, 100], [0, 1, 0])
|
|
130
|
+
return (
|
|
131
|
+
<motion.div
|
|
132
|
+
drag="x"
|
|
133
|
+
dragConstraints={{ left: -100, right: 100 }}
|
|
134
|
+
style={{ x, opacity }}
|
|
135
|
+
>
|
|
136
|
+
Drag me
|
|
137
|
+
</motion.div>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 5. Spring-smoothed pointer
|
|
143
|
+
|
|
144
|
+
`createSpring` mirrors any numeric input with physics smoothing.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { motion, createMotionValue, createSpring } from "solidjs-motion"
|
|
148
|
+
import { onCleanup, onMount } from "solid-js"
|
|
149
|
+
|
|
150
|
+
export function Cursor() {
|
|
151
|
+
const x = createMotionValue(0)
|
|
152
|
+
const y = createMotionValue(0)
|
|
153
|
+
const sx = createSpring(x, { stiffness: 200, damping: 30 })
|
|
154
|
+
const sy = createSpring(y, { stiffness: 200, damping: 30 })
|
|
155
|
+
|
|
156
|
+
onMount(() => {
|
|
157
|
+
const move = (e: PointerEvent) => {
|
|
158
|
+
x.set(e.clientX)
|
|
159
|
+
y.set(e.clientY)
|
|
160
|
+
}
|
|
161
|
+
window.addEventListener("pointermove", move)
|
|
162
|
+
onCleanup(() => window.removeEventListener("pointermove", move))
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return <motion.div class="cursor" style={{ x: sx, y: sy }} />
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 6. Scroll-linked progress bar
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { motion, createScroll, createTransform } from "solidjs-motion"
|
|
173
|
+
|
|
174
|
+
export function ProgressBar() {
|
|
175
|
+
const { scrollYProgress } = createScroll()
|
|
176
|
+
const width = createTransform(scrollYProgress, [0, 1], ["0%", "100%"])
|
|
177
|
+
return <motion.div class="progress" style={{ width }} />
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 7. Viewport-triggered fade-in
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
import { motion } from "solidjs-motion"
|
|
185
|
+
|
|
186
|
+
export function FadeInOnce() {
|
|
187
|
+
return (
|
|
188
|
+
<motion.section
|
|
189
|
+
initial={{ opacity: 0, y: 40 }}
|
|
190
|
+
inView={{ opacity: 1, y: 0 }}
|
|
191
|
+
inViewOptions={{ once: true, margin: "0px 0px -10% 0px" }}
|
|
192
|
+
>
|
|
193
|
+
Comes in once, stays.
|
|
194
|
+
</motion.section>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 8. `createTemplate` for interpolated strings
|
|
200
|
+
|
|
201
|
+
Build a `MotionValue<string>` from interpolated MVs/Accessors — feed it to any string-valued
|
|
202
|
+
CSS property.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { motion, createMotionValue, createTemplate } from "solidjs-motion"
|
|
206
|
+
|
|
207
|
+
export function GradientBox() {
|
|
208
|
+
const angle = createMotionValue(0)
|
|
209
|
+
const background = createTemplate`linear-gradient(${angle}deg, #f0f, #0ff)`
|
|
210
|
+
return (
|
|
211
|
+
<motion.div
|
|
212
|
+
hover={{ rotate: 360 }}
|
|
213
|
+
transition={{ duration: 2 }}
|
|
214
|
+
style={{ background }}
|
|
215
|
+
/>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 9. `<Presence>` for exit animations
|
|
221
|
+
|
|
222
|
+
Wrap a conditionally-rendered child to animate its exit before unmount.
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
import { motion, Presence } from "solidjs-motion"
|
|
226
|
+
import { Show, createSignal } from "solid-js"
|
|
227
|
+
|
|
228
|
+
export function Drawer() {
|
|
229
|
+
const [open, setOpen] = createSignal(false)
|
|
230
|
+
return (
|
|
231
|
+
<>
|
|
232
|
+
<button onClick={() => setOpen((p) => !p)}>Toggle</button>
|
|
233
|
+
<Presence>
|
|
234
|
+
<Show when={open()}>
|
|
235
|
+
<motion.aside
|
|
236
|
+
initial={{ x: -300 }}
|
|
237
|
+
animate={{ x: 0 }}
|
|
238
|
+
exit={{ x: -300 }}
|
|
239
|
+
transition={{ duration: 0.25 }}
|
|
240
|
+
>
|
|
241
|
+
Drawer content
|
|
242
|
+
</motion.aside>
|
|
243
|
+
</Show>
|
|
244
|
+
</Presence>
|
|
245
|
+
</>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 10. `mode="wait"` + list exits
|
|
251
|
+
|
|
252
|
+
`mode="wait"` plays the outgoing child's `exit` fully before the incoming child enters.
|
|
253
|
+
For lists, `Presence` wraps a `<For>` and animates add/remove together.
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { motion, Presence } from "solidjs-motion"
|
|
257
|
+
import { For, Show, createSignal } from "solid-js"
|
|
258
|
+
|
|
259
|
+
export function Tabs() {
|
|
260
|
+
const [tab, setTab] = createSignal("a")
|
|
261
|
+
return (
|
|
262
|
+
<Presence mode="wait">
|
|
263
|
+
<Show when={tab()} keyed>
|
|
264
|
+
{(t) => (
|
|
265
|
+
<motion.div
|
|
266
|
+
initial={{ opacity: 0, y: 8 }}
|
|
267
|
+
animate={{ opacity: 1, y: 0 }}
|
|
268
|
+
exit={{ opacity: 0, y: -8 }}
|
|
269
|
+
>
|
|
270
|
+
Tab {t}
|
|
271
|
+
</motion.div>
|
|
272
|
+
)}
|
|
273
|
+
</Show>
|
|
274
|
+
</Presence>
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function Notifications(props: { items: () => string[] }) {
|
|
279
|
+
return (
|
|
280
|
+
<Presence>
|
|
281
|
+
<For each={props.items()}>
|
|
282
|
+
{(msg) => (
|
|
283
|
+
<motion.li
|
|
284
|
+
initial={{ opacity: 0, x: -16 }}
|
|
285
|
+
animate={{ opacity: 1, x: 0 }}
|
|
286
|
+
exit={{ opacity: 0, x: 16 }}
|
|
287
|
+
>
|
|
288
|
+
{msg}
|
|
289
|
+
</motion.li>
|
|
290
|
+
)}
|
|
291
|
+
</For>
|
|
292
|
+
</Presence>
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 11. Drag with constraints
|
|
298
|
+
|
|
299
|
+
`dragConstraints` accepts numeric bounds or a parent ref. `dragElastic` controls overshoot.
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
import { motion } from "solidjs-motion"
|
|
303
|
+
|
|
304
|
+
export function DraggableCard() {
|
|
305
|
+
let bounds!: HTMLDivElement
|
|
306
|
+
return (
|
|
307
|
+
<div ref={bounds} class="bounds">
|
|
308
|
+
<motion.div
|
|
309
|
+
drag
|
|
310
|
+
dragConstraints={bounds}
|
|
311
|
+
dragElastic={0.2}
|
|
312
|
+
whileDrag={{ scale: 1.05 }}
|
|
313
|
+
>
|
|
314
|
+
Drag inside
|
|
315
|
+
</motion.div>
|
|
316
|
+
</div>
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 12. `<MotionConfig>` + reduced motion
|
|
322
|
+
|
|
323
|
+
`<MotionConfig>` flows defaults (transition, reduced-motion mode, CSP nonce) to descendants.
|
|
324
|
+
`createReducedMotion()` reads the system preference directly.
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
import { MotionConfig, createReducedMotion, motion } from "solidjs-motion"
|
|
328
|
+
|
|
329
|
+
export function App() {
|
|
330
|
+
const reduced = createReducedMotion()
|
|
331
|
+
return (
|
|
332
|
+
<MotionConfig reducedMotion="user" transition={{ duration: 0.4, ease: "easeOut" }}>
|
|
333
|
+
<motion.div animate={{ x: 100 }}>Honors `prefers-reduced-motion`</motion.div>
|
|
334
|
+
<p>System reduced-motion: {String(reduced())}</p>
|
|
335
|
+
</MotionConfig>
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
49
340
|
## Roadmap
|
|
50
341
|
|
|
51
342
|
### Shipped
|
package/dist/index.js
CHANGED
|
@@ -871,6 +871,91 @@ function ensureVisualElement(el) {
|
|
|
871
871
|
return ve;
|
|
872
872
|
}
|
|
873
873
|
/**
|
|
874
|
+
* Parse the visible translate from an element's transform — reads both
|
|
875
|
+
* `getComputedStyle(el).transform` (which real browsers normalize to
|
|
876
|
+
* matrix form) AND `el.style.transform` (which preserves the raw syntax
|
|
877
|
+
* motion-dom's writer emits, e.g. `translate3d(50px, 0px, 0)`). Used by
|
|
878
|
+
* drag's pan-start to sync the x/y MotionValues to what the user is
|
|
879
|
+
* actually seeing.
|
|
880
|
+
*
|
|
881
|
+
* Why both sources: motion's `animate(el, target)` interpolates style.
|
|
882
|
+
* transform via WAAPI but DOESN'T update the VE's MVs during the tween,
|
|
883
|
+
* so after `initial: {x:-300} → animate: {x:0}` the MV still holds -300.
|
|
884
|
+
* Reading the current transform recovers the truth. We prefer computed
|
|
885
|
+
* (post-animation, post-WAAPI-commit value) and fall back to inline
|
|
886
|
+
* (covers jsdom + cases where motion's writer wrote inline but the
|
|
887
|
+
* browser hasn't run a style-resolve pass yet).
|
|
888
|
+
*
|
|
889
|
+
* Supported syntaxes:
|
|
890
|
+
* - `"none"` / empty → {0, 0}
|
|
891
|
+
* - `matrix(a, b, c, d, tx, ty)`
|
|
892
|
+
* - `matrix3d(..., tx, ty, ...)`
|
|
893
|
+
* - `translateX(Npx)` / `translateY(Npx)` / `translate(tx, ty)`
|
|
894
|
+
* - `translate3d(tx, ty, tz)`
|
|
895
|
+
* - Any of the above mixed with other transform functions (regex-
|
|
896
|
+
* based extraction picks just the translate components).
|
|
897
|
+
*/
|
|
898
|
+
function readVisibleTranslate(el) {
|
|
899
|
+
const fromString = (transform) => {
|
|
900
|
+
if (!transform || transform === "none") return null;
|
|
901
|
+
if (transform.startsWith("matrix3d(")) {
|
|
902
|
+
const values = transform.slice(9, -1).split(",");
|
|
903
|
+
const tx = Number.parseFloat(values[12] ?? "0");
|
|
904
|
+
const ty = Number.parseFloat(values[13] ?? "0");
|
|
905
|
+
if (!Number.isFinite(tx) && !Number.isFinite(ty)) return null;
|
|
906
|
+
return {
|
|
907
|
+
x: Number.isFinite(tx) ? tx : 0,
|
|
908
|
+
y: Number.isFinite(ty) ? ty : 0
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
if (transform.startsWith("matrix(")) {
|
|
912
|
+
const values = transform.slice(7, -1).split(",");
|
|
913
|
+
const tx = Number.parseFloat(values[4] ?? "0");
|
|
914
|
+
const ty = Number.parseFloat(values[5] ?? "0");
|
|
915
|
+
if (!Number.isFinite(tx) && !Number.isFinite(ty)) return null;
|
|
916
|
+
return {
|
|
917
|
+
x: Number.isFinite(tx) ? tx : 0,
|
|
918
|
+
y: Number.isFinite(ty) ? ty : 0
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
let x = 0;
|
|
922
|
+
let y = 0;
|
|
923
|
+
let found = false;
|
|
924
|
+
const translate3d = transform.match(/translate3d\(\s*([-\d.]+)px\s*,\s*([-\d.]+)px/);
|
|
925
|
+
if (translate3d) {
|
|
926
|
+
x = Number.parseFloat(translate3d[1] ?? "0");
|
|
927
|
+
y = Number.parseFloat(translate3d[2] ?? "0");
|
|
928
|
+
found = true;
|
|
929
|
+
} else {
|
|
930
|
+
const translate2d = transform.match(/translate\(\s*([-\d.]+)px\s*(?:,\s*([-\d.]+)px)?/);
|
|
931
|
+
if (translate2d) {
|
|
932
|
+
x = Number.parseFloat(translate2d[1] ?? "0");
|
|
933
|
+
y = Number.parseFloat(translate2d[2] ?? "0");
|
|
934
|
+
found = true;
|
|
935
|
+
}
|
|
936
|
+
const translateX = transform.match(/translateX\(\s*([-\d.]+)px/);
|
|
937
|
+
if (translateX) {
|
|
938
|
+
x = Number.parseFloat(translateX[1] ?? "0");
|
|
939
|
+
found = true;
|
|
940
|
+
}
|
|
941
|
+
const translateY = transform.match(/translateY\(\s*([-\d.]+)px/);
|
|
942
|
+
if (translateY) {
|
|
943
|
+
y = Number.parseFloat(translateY[1] ?? "0");
|
|
944
|
+
found = true;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (!found) return null;
|
|
948
|
+
return {
|
|
949
|
+
x: Number.isFinite(x) ? x : 0,
|
|
950
|
+
y: Number.isFinite(y) ? y : 0
|
|
951
|
+
};
|
|
952
|
+
};
|
|
953
|
+
return fromString(getComputedStyle(el).transform) ?? fromString(el.style.transform) ?? {
|
|
954
|
+
x: 0,
|
|
955
|
+
y: 0
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
874
959
|
* Compute the `touch-action` CSS value for an element being dragged.
|
|
875
960
|
* Disabling touch-action prevents the browser from interpreting the gesture
|
|
876
961
|
* as a scroll. Axis-locked drags leave the unused axis available for scroll
|
|
@@ -1008,14 +1093,27 @@ function createDrag(el, getOpts, setActive) {
|
|
|
1008
1093
|
for (const ctrl of momentumControls) ctrl.stop();
|
|
1009
1094
|
momentumControls = [];
|
|
1010
1095
|
}
|
|
1011
|
-
const handlePanStart = (event, info) => {
|
|
1096
|
+
const handlePanStart = (event, info, mvIsAuthoritative = false) => {
|
|
1012
1097
|
if (!isDragEnabled()) return;
|
|
1013
1098
|
stopMomentum();
|
|
1014
1099
|
const ve = ensureVisualElement(el);
|
|
1015
1100
|
xMV = ve.getValue("x", 0);
|
|
1016
1101
|
yMV = ve.getValue("y", 0);
|
|
1017
|
-
|
|
1018
|
-
|
|
1102
|
+
const axis = getOpts().drag;
|
|
1103
|
+
if (mvIsAuthoritative) {
|
|
1104
|
+
dragStartX = xMV.get();
|
|
1105
|
+
dragStartY = yMV.get();
|
|
1106
|
+
} else {
|
|
1107
|
+
const visible = readVisibleTranslate(el);
|
|
1108
|
+
if (axis !== "y") {
|
|
1109
|
+
if (visible.x !== xMV.get()) xMV.set(visible.x);
|
|
1110
|
+
dragStartX = visible.x;
|
|
1111
|
+
} else dragStartX = xMV.get();
|
|
1112
|
+
if (axis !== "x") {
|
|
1113
|
+
if (visible.y !== yMV.get()) yMV.set(visible.y);
|
|
1114
|
+
dragStartY = visible.y;
|
|
1115
|
+
} else dragStartY = yMV.get();
|
|
1116
|
+
}
|
|
1019
1117
|
sessionBounds = resolveConstraints(getOpts().dragConstraints, el, dragStartX, dragStartY);
|
|
1020
1118
|
savedUserSelect = document.body.style.userSelect;
|
|
1021
1119
|
savedTouchAction = el.style.touchAction;
|
|
@@ -1184,7 +1282,7 @@ function createDrag(el, getOpts, setActive) {
|
|
|
1184
1282
|
x: 0,
|
|
1185
1283
|
y: 0
|
|
1186
1284
|
}
|
|
1187
|
-
});
|
|
1285
|
+
}, Boolean(options.snapToCursor));
|
|
1188
1286
|
const sessionStartPoint = {
|
|
1189
1287
|
x: event.clientX,
|
|
1190
1288
|
y: event.clientY
|
|
@@ -1493,7 +1591,7 @@ function createGestureStateMachine(deps) {
|
|
|
1493
1591
|
});
|
|
1494
1592
|
const winners = createMemo(() => {
|
|
1495
1593
|
const targets = stateTargets();
|
|
1496
|
-
const
|
|
1594
|
+
const dragActive = active.whileDrag;
|
|
1497
1595
|
const out = {};
|
|
1498
1596
|
for (const stateName of PRIORITY_HIGH_TO_LOW) {
|
|
1499
1597
|
if (!isStateActive(stateName, active, parentVariantCtx)) continue;
|
|
@@ -1502,7 +1600,7 @@ function createGestureStateMachine(deps) {
|
|
|
1502
1600
|
for (const key in target) {
|
|
1503
1601
|
if (key === "transition") continue;
|
|
1504
1602
|
if (key in out) continue;
|
|
1505
|
-
if (!active.exit &&
|
|
1603
|
+
if (!active.exit && dragActive && (key === "x" || key === "y")) continue;
|
|
1506
1604
|
out[key] = {
|
|
1507
1605
|
value: target[key],
|
|
1508
1606
|
transition: target.transition,
|
|
@@ -1545,6 +1643,7 @@ function createGestureStateMachine(deps) {
|
|
|
1545
1643
|
}
|
|
1546
1644
|
for (const key in lastApplied) {
|
|
1547
1645
|
if (key in next) continue;
|
|
1646
|
+
if (active.whileDrag && (key === "x" || key === "y")) continue;
|
|
1548
1647
|
const initialValue = initialTarget && key in initialTarget ? initialTarget[key] : void 0;
|
|
1549
1648
|
changes[key] = initialValue !== void 0 ? initialValue : getMotionDefault(key);
|
|
1550
1649
|
}
|