@rafaelaugusto/jolt 0.1.0-beta.11 → 0.1.0-beta.13
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 +42 -8
- package/dist/animate.d.ts +15 -3
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +52 -34
- package/dist/animate.js.map +1 -1
- package/dist/drag.d.ts +17 -3
- package/dist/drag.d.ts.map +1 -1
- package/dist/drag.js +34 -11
- package/dist/drag.js.map +1 -1
- package/dist/driver/android.d.ts.map +1 -1
- package/dist/driver/android.js +2 -0
- package/dist/driver/android.js.map +1 -1
- package/dist/driver/frame.d.ts +3 -0
- package/dist/driver/frame.d.ts.map +1 -0
- package/dist/driver/frame.js +7 -0
- package/dist/driver/frame.js.map +1 -0
- package/dist/driver/index.d.ts +2 -0
- package/dist/driver/index.d.ts.map +1 -1
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/ios.d.ts.map +1 -1
- package/dist/driver/ios.js +2 -0
- package/dist/driver/ios.js.map +1 -1
- package/dist/easing.d.ts +1 -1
- package/dist/easing.d.ts.map +1 -1
- package/dist/easing.js +11 -0
- package/dist/easing.js.map +1 -1
- package/dist/engine.d.ts +20 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +166 -38
- package/dist/engine.js.map +1 -1
- package/dist/gradient.d.ts.map +1 -1
- package/dist/gradient.js +10 -4
- package/dist/gradient.js.map +1 -1
- package/dist/image-effect.d.ts +1 -0
- package/dist/image-effect.d.ts.map +1 -1
- package/dist/image-effect.js +10 -2
- package/dist/image-effect.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js +63 -10
- package/dist/keyboard.js.map +1 -1
- package/dist/mixer.d.ts +49 -0
- package/dist/mixer.d.ts.map +1 -0
- package/dist/mixer.js +238 -0
- package/dist/mixer.js.map +1 -0
- package/dist/motion-scope.d.ts +6 -2
- package/dist/motion-scope.d.ts.map +1 -1
- package/dist/motion-scope.js +12 -4
- package/dist/motion-scope.js.map +1 -1
- package/dist/motion-states.d.ts +10 -5
- package/dist/motion-states.d.ts.map +1 -1
- package/dist/motion-states.js +55 -22
- package/dist/motion-states.js.map +1 -1
- package/dist/motion-value.d.ts +152 -0
- package/dist/motion-value.d.ts.map +1 -0
- package/dist/motion-value.js +635 -0
- package/dist/motion-value.js.map +1 -0
- package/dist/native/index.d.ts +6 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +3 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native-properties.d.ts +28 -0
- package/dist/native-properties.d.ts.map +1 -0
- package/dist/native-properties.js +154 -0
- package/dist/native-properties.js.map +1 -0
- package/dist/native-property-driver.d.ts +19 -0
- package/dist/native-property-driver.d.ts.map +1 -0
- package/dist/native-property-driver.js +69 -0
- package/dist/native-property-driver.js.map +1 -0
- package/dist/native-property.d.ts +35 -0
- package/dist/native-property.d.ts.map +1 -0
- package/dist/native-property.js +277 -0
- package/dist/native-property.js.map +1 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +2 -2
- package/dist/path.js.map +1 -1
- package/dist/playback-group.d.ts.map +1 -1
- package/dist/playback-group.js +17 -1
- package/dist/playback-group.js.map +1 -1
- package/dist/playback.d.ts +15 -0
- package/dist/playback.d.ts.map +1 -1
- package/dist/playback.js +23 -0
- package/dist/playback.js.map +1 -1
- package/dist/sequence.d.ts +24 -0
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +181 -19
- package/dist/sequence.js.map +1 -1
- package/dist/timed-animation.d.ts +38 -0
- package/dist/timed-animation.d.ts.map +1 -0
- package/dist/timed-animation.js +177 -0
- package/dist/timed-animation.js.map +1 -0
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +31 -12
- package/dist/track.js.map +1 -1
- package/dist/transition.d.ts +15 -4
- package/dist/transition.d.ts.map +1 -1
- package/dist/transition.js +46 -7
- package/dist/transition.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/value-observers.d.ts +19 -0
- package/dist/value-observers.d.ts.map +1 -0
- package/dist/value-observers.js +111 -0
- package/dist/value-observers.js.map +1 -0
- package/dist/value-track.d.ts +13 -0
- package/dist/value-track.d.ts.map +1 -0
- package/dist/value-track.js +59 -0
- package/dist/value-track.js.map +1 -0
- package/dist/vue/index.d.ts +28 -2
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +78 -2
- package/dist/vue/index.js.map +1 -1
- package/package.json +6 -2
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrame.java +53 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrameFollower.java +57 -0
- package/src/android-plugin.d.ts +64 -0
- package/src/animate.ts +106 -17
- package/src/drag.ts +67 -21
- package/src/driver/android.ts +2 -0
- package/src/driver/frame.ts +10 -0
- package/src/driver/index.ts +2 -0
- package/src/driver/ios.ts +2 -0
- package/src/easing.ts +14 -0
- package/src/engine.ts +181 -55
- package/src/gradient.ts +10 -4
- package/src/image-effect.ts +10 -2
- package/src/index.ts +23 -1
- package/src/ios-plugin.d.ts +33 -0
- package/src/keyboard.ts +83 -18
- package/src/mixer.ts +264 -0
- package/src/motion-scope.ts +17 -5
- package/src/motion-states.ts +79 -29
- package/src/motion-value.ts +795 -0
- package/src/native/index.ts +16 -0
- package/src/native-properties.ts +188 -0
- package/src/native-property-driver.ts +123 -0
- package/src/native-property.ts +353 -0
- package/src/path.ts +2 -1
- package/src/playback-group.ts +17 -1
- package/src/playback.ts +41 -0
- package/src/sequence.ts +209 -20
- package/src/timed-animation.ts +192 -0
- package/src/track.ts +37 -12
- package/src/transition.ts +69 -10
- package/src/types.ts +5 -0
- package/src/value-observers.ts +133 -0
- package/src/value-track.ts +71 -0
- package/src/vue/index.ts +98 -1
package/src/android-plugin.d.ts
CHANGED
|
@@ -136,6 +136,33 @@ declare namespace org.rafaelaugusto.jolt {
|
|
|
136
136
|
})
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
class KeyboardFrame {
|
|
140
|
+
constructor(root: android.view.View, listener: KeyboardFrame.Listener)
|
|
141
|
+
attach(): void
|
|
142
|
+
detach(): void
|
|
143
|
+
height(): number
|
|
144
|
+
}
|
|
145
|
+
namespace KeyboardFrame {
|
|
146
|
+
class Listener {
|
|
147
|
+
constructor(implementation: { onChange(height: number): void })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
class KeyboardFrameFollower {
|
|
151
|
+
constructor(
|
|
152
|
+
view: android.view.View,
|
|
153
|
+
offset: number,
|
|
154
|
+
listener: KeyboardFrameFollower.Listener,
|
|
155
|
+
)
|
|
156
|
+
attach(): void
|
|
157
|
+
detach(): void
|
|
158
|
+
}
|
|
159
|
+
namespace KeyboardFrameFollower {
|
|
160
|
+
class Listener {
|
|
161
|
+
constructor(implementation: {
|
|
162
|
+
onMove(from: number, to: number, keyboard: number): void
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
139
166
|
class TextReveal {
|
|
140
167
|
constructor(
|
|
141
168
|
view: android.widget.TextView,
|
|
@@ -182,3 +209,40 @@ declare namespace org.rafaelaugusto.jolt {
|
|
|
182
209
|
}
|
|
183
210
|
}
|
|
184
211
|
}
|
|
212
|
+
|
|
213
|
+
declare namespace org.rafaelaugusto.jolt {
|
|
214
|
+
class PropertySink extends java.lang.Object {
|
|
215
|
+
static scalar(target: any, getter: string, setter: string): PropertySink
|
|
216
|
+
getDimensions(): number
|
|
217
|
+
isAlive(): boolean
|
|
218
|
+
isCompiled(): boolean
|
|
219
|
+
read(): native.Array<number>
|
|
220
|
+
write(values: native.Array<number>): void
|
|
221
|
+
dispose(): void
|
|
222
|
+
}
|
|
223
|
+
class MapLibrePropertySink extends PropertySink {
|
|
224
|
+
constructor(target: any, camera: boolean)
|
|
225
|
+
}
|
|
226
|
+
class PropertyPlayer extends java.lang.Object {
|
|
227
|
+
constructor(
|
|
228
|
+
sink: PropertySink,
|
|
229
|
+
times: native.Array<number>,
|
|
230
|
+
values: native.Array<number>,
|
|
231
|
+
finalValue: native.Array<number>,
|
|
232
|
+
duration: number,
|
|
233
|
+
delay: number,
|
|
234
|
+
repeat: number,
|
|
235
|
+
reverse: boolean,
|
|
236
|
+
listener: PropertyPlayer.Listener,
|
|
237
|
+
)
|
|
238
|
+
static activeCount(): number
|
|
239
|
+
getFrames(): number
|
|
240
|
+
control(time: number, paused: boolean): void
|
|
241
|
+
cancel(): void
|
|
242
|
+
}
|
|
243
|
+
namespace PropertyPlayer {
|
|
244
|
+
class Listener extends java.lang.Object {
|
|
245
|
+
constructor(implementation: { onEnd(completed: boolean): void })
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
package/src/animate.ts
CHANGED
|
@@ -17,16 +17,26 @@ import { DEFAULT_FRICTION, solveDecay } from './decay.js'
|
|
|
17
17
|
import {
|
|
18
18
|
arrivalOf,
|
|
19
19
|
setArrival,
|
|
20
|
+
stopLayer,
|
|
20
21
|
stopView,
|
|
21
22
|
ViewAnimation,
|
|
22
23
|
type Animation,
|
|
23
24
|
type Plan,
|
|
24
25
|
} from './engine.js'
|
|
25
26
|
import { getPreset, type PresetName } from './presets.js'
|
|
27
|
+
import {
|
|
28
|
+
asRoot,
|
|
29
|
+
detachBindings,
|
|
30
|
+
isMotionValue,
|
|
31
|
+
type MotionValue,
|
|
32
|
+
type ValueAnimationOptions,
|
|
33
|
+
} from './motion-value.js'
|
|
26
34
|
import { resolveHaptic, type Haptic } from './haptics.js'
|
|
35
|
+
import { layerable } from './mixer.js'
|
|
27
36
|
import { isProperty, precision, type Property } from './properties.js'
|
|
28
37
|
import {
|
|
29
38
|
hasCurve,
|
|
39
|
+
fitTransition,
|
|
30
40
|
mergeTransitions,
|
|
31
41
|
resolveTransition,
|
|
32
42
|
springs,
|
|
@@ -51,24 +61,45 @@ interface Options {
|
|
|
51
61
|
stagger: number | Stagger | undefined
|
|
52
62
|
relative: boolean | undefined
|
|
53
63
|
haptic: Haptic | undefined
|
|
64
|
+
layer?: string
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
/**
|
|
57
68
|
* Animates one view, or several. Takes keyframes or the name of a preset,
|
|
58
|
-
* and a transition or the name of a spring.
|
|
69
|
+
* and a transition or the name of a spring. A motion value animates to a number.
|
|
59
70
|
*
|
|
60
71
|
* ```ts
|
|
61
72
|
* animate(card, 'fadeInUp')
|
|
62
73
|
* animate(card, { scale: 1.1, rotate: [0, 90] }, 'bouncy')
|
|
63
74
|
* animate(rows, 'fadeInUp', { stagger: 40 })
|
|
75
|
+
* animate(progress, 1, 'snappy')
|
|
64
76
|
* ```
|
|
65
77
|
*/
|
|
78
|
+
export function animate(
|
|
79
|
+
value: MotionValue,
|
|
80
|
+
to: number | readonly (number | null)[],
|
|
81
|
+
transition?: ValueAnimationOptions | SpringName,
|
|
82
|
+
): PlaybackAnimation
|
|
66
83
|
export function animate(
|
|
67
84
|
target: Target | readonly Target[],
|
|
68
85
|
animation: Keyframes | PresetName,
|
|
69
86
|
options?: AnimateOptions | SpringName,
|
|
70
|
-
): PlaybackAnimation
|
|
71
|
-
|
|
87
|
+
): PlaybackAnimation
|
|
88
|
+
export function animate(
|
|
89
|
+
target: Target | readonly Target[] | MotionValue,
|
|
90
|
+
animation: Keyframes | PresetName | number | readonly (number | null)[],
|
|
91
|
+
options?: AnimateOptions | SpringName,
|
|
92
|
+
): PlaybackAnimation | Animation {
|
|
93
|
+
if (isMotionValue(target))
|
|
94
|
+
return asRoot(target).animate(
|
|
95
|
+
animation as number | readonly (number | null)[],
|
|
96
|
+
options as ValueAnimationOptions | SpringName | undefined,
|
|
97
|
+
)
|
|
98
|
+
return prepareAnimation(
|
|
99
|
+
target as Target | readonly Target[],
|
|
100
|
+
animation as Keyframes | PresetName,
|
|
101
|
+
options,
|
|
102
|
+
)()
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
/** Validate and snapshot a complete animation before changing any view ownership. */
|
|
@@ -77,7 +108,7 @@ export function prepareAnimation(
|
|
|
77
108
|
animation: Keyframes | PresetName,
|
|
78
109
|
options?: AnimateOptions | SpringName,
|
|
79
110
|
): () => PlaybackAnimation {
|
|
80
|
-
const { keyframes, base, overrides, stagger, relative, haptic, path } = prepare(
|
|
111
|
+
const { keyframes, base, overrides, stagger, relative, haptic, path, layer } = prepare(
|
|
81
112
|
animation,
|
|
82
113
|
options,
|
|
83
114
|
)
|
|
@@ -88,8 +119,15 @@ export function prepareAnimation(
|
|
|
88
119
|
for (const property of Object.keys(keyframes))
|
|
89
120
|
if (isColorProperty(property)) validateColorTarget(view, property)
|
|
90
121
|
else if (isDecorationProperty(property)) validateDecorationTarget(view, property)
|
|
91
|
-
const plans = plan(keyframes, base, overrides, 0, relative, path)
|
|
92
|
-
return () =>
|
|
122
|
+
const plans = plan(keyframes, base, overrides, 0, relative, path, layer)
|
|
123
|
+
return () => {
|
|
124
|
+
if (layer === undefined)
|
|
125
|
+
detachBindings(
|
|
126
|
+
view,
|
|
127
|
+
plans.map(({ property }) => property),
|
|
128
|
+
)
|
|
129
|
+
return new ViewAnimation(view, plans, feedback)
|
|
130
|
+
}
|
|
93
131
|
}
|
|
94
132
|
|
|
95
133
|
const views = target.map(toView)
|
|
@@ -97,6 +135,7 @@ export function prepareAnimation(
|
|
|
97
135
|
for (const property of Object.keys(keyframes))
|
|
98
136
|
if (isColorProperty(property)) validateColorTarget(view, property)
|
|
99
137
|
else if (isDecorationProperty(property)) validateDecorationTarget(view, property)
|
|
138
|
+
const seed = Math.random()
|
|
100
139
|
const plans = views.map((_, index) =>
|
|
101
140
|
plan(
|
|
102
141
|
keyframes,
|
|
@@ -105,11 +144,20 @@ export function prepareAnimation(
|
|
|
105
144
|
staggerDelay(stagger, index, views.length),
|
|
106
145
|
relative,
|
|
107
146
|
path,
|
|
147
|
+
layer,
|
|
148
|
+
seed,
|
|
108
149
|
),
|
|
109
150
|
)
|
|
110
151
|
return () =>
|
|
111
152
|
playbackGroup(
|
|
112
|
-
views.map((view, index) =>
|
|
153
|
+
views.map((view, index) => {
|
|
154
|
+
if (layer === undefined)
|
|
155
|
+
detachBindings(
|
|
156
|
+
view,
|
|
157
|
+
plans[index]!.map(({ property }) => property),
|
|
158
|
+
)
|
|
159
|
+
return new ViewAnimation(view, plans[index]!, feedback, true)
|
|
160
|
+
}),
|
|
113
161
|
)
|
|
114
162
|
}
|
|
115
163
|
|
|
@@ -186,15 +234,33 @@ export function decay(
|
|
|
186
234
|
)
|
|
187
235
|
}
|
|
188
236
|
|
|
189
|
-
/**
|
|
237
|
+
/**
|
|
238
|
+
* Stops all or selected properties at their current values. `scale` stops both axes.
|
|
239
|
+
* A motion value stops where it is, and so do the views bound to it.
|
|
240
|
+
*/
|
|
241
|
+
export function stop(value: MotionValue, options?: { layer?: string }): void
|
|
190
242
|
export function stop(
|
|
191
243
|
target: Target | readonly Target[],
|
|
192
244
|
properties?: readonly KeyframeProperty[],
|
|
245
|
+
options?: { layer?: string },
|
|
246
|
+
): void
|
|
247
|
+
export function stop(
|
|
248
|
+
target: Target | readonly Target[] | MotionValue,
|
|
249
|
+
properties?: readonly KeyframeProperty[] | { layer?: string },
|
|
250
|
+
options?: { layer?: string },
|
|
193
251
|
) {
|
|
194
|
-
|
|
252
|
+
if (isMotionValue(target))
|
|
253
|
+
return asRoot(target).stop((properties as { layer?: string } | undefined)?.layer)
|
|
254
|
+
if (properties && !Array.isArray(properties))
|
|
255
|
+
throw new TypeError('View properties must be an array.')
|
|
256
|
+
const expanded = (
|
|
257
|
+
properties as readonly KeyframeProperty[] | undefined
|
|
258
|
+
)?.flatMap<Property>((property) =>
|
|
195
259
|
property === 'scale' ? ['scaleX', 'scaleY'] : [property],
|
|
196
260
|
)
|
|
197
|
-
for (const view of toViews(target
|
|
261
|
+
for (const view of toViews(target as Target | readonly Target[]))
|
|
262
|
+
if (options?.layer !== undefined) stopLayer(view, options.layer, expanded)
|
|
263
|
+
else stopView(view, expanded)
|
|
198
264
|
}
|
|
199
265
|
|
|
200
266
|
/**
|
|
@@ -205,7 +271,13 @@ export function stop(
|
|
|
205
271
|
* set(logo, { opacity: 1, scale: 1, y: 0 })
|
|
206
272
|
* ```
|
|
207
273
|
*/
|
|
208
|
-
export function set(
|
|
274
|
+
export function set(value: MotionValue, to: number): void
|
|
275
|
+
export function set(target: Target | readonly Target[], values: Values): void
|
|
276
|
+
export function set(
|
|
277
|
+
target: Target | readonly Target[] | MotionValue,
|
|
278
|
+
values: Values | number,
|
|
279
|
+
) {
|
|
280
|
+
if (isMotionValue(target)) return asRoot(target).set(values as number)
|
|
209
281
|
if (values === null || typeof values !== 'object' || Array.isArray(values))
|
|
210
282
|
throw new TypeError('set() takes an object of values.')
|
|
211
283
|
const keyframes: Record<string, unknown> = {}
|
|
@@ -218,7 +290,7 @@ export function set(target: Target | readonly Target[], values: Values): void {
|
|
|
218
290
|
)
|
|
219
291
|
keyframes[key] = [value, value]
|
|
220
292
|
}
|
|
221
|
-
animate(target, keyframes as Keyframes, { duration: 0 })
|
|
293
|
+
animate(target as Target | readonly Target[], keyframes as Keyframes, { duration: 0 })
|
|
222
294
|
}
|
|
223
295
|
|
|
224
296
|
/** Plays several animations as one: finished once they all are, and stopped together. */
|
|
@@ -281,6 +353,7 @@ function resolveAnimation(
|
|
|
281
353
|
stagger: user.stagger ?? own.stagger,
|
|
282
354
|
relative: user.relative ?? own.relative,
|
|
283
355
|
haptic: user.haptic ?? own.haptic,
|
|
356
|
+
layer: user.layer,
|
|
284
357
|
}
|
|
285
358
|
}
|
|
286
359
|
|
|
@@ -307,16 +380,21 @@ function normalize(options: AnimateOptions | SpringName | undefined): Options {
|
|
|
307
380
|
let stagger: Options['stagger']
|
|
308
381
|
let relative: Options['relative']
|
|
309
382
|
let haptic: Options['haptic']
|
|
383
|
+
let layer: Options['layer']
|
|
310
384
|
for (const [key, value] of Object.entries(options)) {
|
|
311
385
|
if (value === undefined) continue
|
|
312
|
-
if (key === '
|
|
386
|
+
if (key === 'layer') {
|
|
387
|
+
if (typeof value !== 'string' || !value)
|
|
388
|
+
throw new TypeError('A layer is named by a nonempty string.')
|
|
389
|
+
layer = value
|
|
390
|
+
} else if (key === 'stagger') stagger = value as Options['stagger']
|
|
313
391
|
else if (key === 'relative') relative = value as Options['relative']
|
|
314
392
|
else if (key === 'haptic') haptic = value as Options['haptic']
|
|
315
393
|
else if (key === 'path' || key === 'scale' || isProperty(key)) {
|
|
316
394
|
overrides[key] = typeof value === 'string' ? spring(value as SpringName) : value
|
|
317
395
|
} else base[key] = value
|
|
318
396
|
}
|
|
319
|
-
return { base, overrides, stagger, relative, haptic }
|
|
397
|
+
return { base, overrides, stagger, relative, haptic, layer }
|
|
320
398
|
}
|
|
321
399
|
|
|
322
400
|
function plan(
|
|
@@ -326,8 +404,16 @@ function plan(
|
|
|
326
404
|
offset: number,
|
|
327
405
|
relative = false,
|
|
328
406
|
path?: PreparedPath,
|
|
407
|
+
layer?: string,
|
|
408
|
+
seed = Math.random(),
|
|
329
409
|
): Plan[] {
|
|
410
|
+
if (layer !== undefined && path)
|
|
411
|
+
throw new Error("A path owns x and y; it can't play on a layer.")
|
|
330
412
|
return expand(keyframes, path).map(([property, frames]) => {
|
|
413
|
+
if (layer !== undefined && !layerable(property))
|
|
414
|
+
throw new Error(
|
|
415
|
+
`Layers apply to x, y, scale, rotation and opacity; "${property}" has a single owner.`,
|
|
416
|
+
)
|
|
331
417
|
if (path?.properties.includes(property as 'x' | 'y' | 'rotate')) {
|
|
332
418
|
if (
|
|
333
419
|
!Number.isFinite(path.transition.delay + offset) ||
|
|
@@ -347,12 +433,13 @@ function plan(
|
|
|
347
433
|
const override =
|
|
348
434
|
overrides[property] ?? (property.startsWith('scale') ? overrides.scale : undefined)
|
|
349
435
|
const transition = resolveTransition(
|
|
350
|
-
override ? mergeTransitions(base, override) : base,
|
|
436
|
+
fitTransition(override ? mergeTransitions(base, override) : base, frames.length),
|
|
351
437
|
property,
|
|
352
438
|
frames.length,
|
|
353
439
|
)
|
|
354
440
|
transition.delay += offset
|
|
355
|
-
|
|
441
|
+
if (transition.seed !== undefined) transition.seed = seed
|
|
442
|
+
return { property, keyframes: frames, transition, relative, layer }
|
|
356
443
|
})
|
|
357
444
|
}
|
|
358
445
|
|
|
@@ -361,8 +448,10 @@ export function startingValues(
|
|
|
361
448
|
animation: Keyframes | PresetName,
|
|
362
449
|
options?: AnimateOptions | SpringName,
|
|
363
450
|
) {
|
|
364
|
-
const { keyframes, relative, path } = prepare(animation, options)
|
|
451
|
+
const { keyframes, relative, path, layer } = prepare(animation, options)
|
|
365
452
|
if (!keyframes) throw new Error(`Unknown preset "${animation}".`)
|
|
453
|
+
if (layer !== undefined)
|
|
454
|
+
return new Map(expand(keyframes, path).map(([property]) => [property, null]))
|
|
366
455
|
return new Map(
|
|
367
456
|
expand(keyframes, path).map(([property, [first]]) => [
|
|
368
457
|
property,
|
package/src/drag.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type TouchGestureEventData,
|
|
6
6
|
type View,
|
|
7
7
|
} from '@nativescript/core'
|
|
8
|
-
import {
|
|
8
|
+
import { toView } from './animate.js'
|
|
9
9
|
import { DEFAULT_FRICTION } from './decay.js'
|
|
10
10
|
import { DragInput } from './drag-input.js'
|
|
11
11
|
import {
|
|
@@ -15,9 +15,18 @@ import {
|
|
|
15
15
|
solveDragRelease,
|
|
16
16
|
type DragCurve,
|
|
17
17
|
} from './drag-physics.js'
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
baseValue,
|
|
20
|
+
currentValue,
|
|
21
|
+
setBase,
|
|
22
|
+
stopBase,
|
|
23
|
+
ViewAnimation,
|
|
24
|
+
type Animation,
|
|
25
|
+
type Plan,
|
|
26
|
+
} from './engine.js'
|
|
27
|
+
import { asRoot, isMotionValue, type MotionValue } from './motion-value.js'
|
|
19
28
|
import type { PlaybackAnimation } from './playback.js'
|
|
20
|
-
import {
|
|
29
|
+
import { createTrack } from './track.js'
|
|
21
30
|
import type { Target } from './types.js'
|
|
22
31
|
|
|
23
32
|
export interface DragPoint {
|
|
@@ -29,10 +38,10 @@ export interface DragEvent extends DragPoint {
|
|
|
29
38
|
readonly velocityX: number
|
|
30
39
|
readonly velocityY: number
|
|
31
40
|
}
|
|
32
|
-
export interface DragRelease extends DragEvent {
|
|
41
|
+
export interface DragRelease<A extends Animation = PlaybackAnimation> extends DragEvent {
|
|
33
42
|
readonly target: DragPoint
|
|
34
43
|
/** False on interruption/cancellation. Check before applying completion effects. */
|
|
35
|
-
readonly animation:
|
|
44
|
+
readonly animation: A
|
|
36
45
|
}
|
|
37
46
|
export interface DragOptions {
|
|
38
47
|
/** Defaults to both. One finger only. */
|
|
@@ -51,6 +60,17 @@ export interface DragOptions {
|
|
|
51
60
|
onCancel?: (event: DragEvent) => void
|
|
52
61
|
onSettle?: (position: DragPoint) => void
|
|
53
62
|
}
|
|
63
|
+
/** Drags a motion value instead of the view. Bounds and snap points are in the value's units. */
|
|
64
|
+
export interface DragValueOptions extends Omit<
|
|
65
|
+
DragOptions,
|
|
66
|
+
'axis' | 'snap' | 'onRelease'
|
|
67
|
+
> {
|
|
68
|
+
/** Takes the position on every move and plays the release; bind views to it to follow. */
|
|
69
|
+
value: MotionValue
|
|
70
|
+
axis: 'x' | 'y'
|
|
71
|
+
snap?: readonly number[]
|
|
72
|
+
onRelease?: (event: DragRelease<Animation>) => void
|
|
73
|
+
}
|
|
54
74
|
export interface Drag {
|
|
55
75
|
readonly dragging: boolean
|
|
56
76
|
readonly settling: boolean
|
|
@@ -67,22 +87,28 @@ export interface Drag {
|
|
|
67
87
|
}
|
|
68
88
|
|
|
69
89
|
type Axis = 'x' | 'y'
|
|
90
|
+
type Root = ReturnType<typeof asRoot>
|
|
70
91
|
interface Settings {
|
|
71
92
|
axes: Axis[]
|
|
72
93
|
curves: Record<Axis, DragCurve>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
value: Root | undefined
|
|
95
|
+
callbacks: Pick<DragOptions, 'onStart' | 'onMove' | 'onCancel' | 'onSettle'> & {
|
|
96
|
+
onRelease?: (event: DragRelease<Animation>) => void
|
|
97
|
+
}
|
|
77
98
|
}
|
|
78
99
|
const owners = new WeakMap<View, Drag>()
|
|
79
100
|
|
|
80
|
-
function settings(options: DragOptions): Settings {
|
|
101
|
+
function settings(options: DragOptions | DragValueOptions): Settings {
|
|
81
102
|
if (!options || typeof options !== 'object')
|
|
82
103
|
throw new TypeError('Drag options must be an object.')
|
|
83
104
|
const axis = options.axis ?? 'both'
|
|
84
105
|
if (!['x', 'y', 'both'].includes(axis))
|
|
85
106
|
throw new TypeError('Drag axis must be x, y or both.')
|
|
107
|
+
const value = 'value' in options ? options.value : undefined
|
|
108
|
+
if (value !== undefined && !isMotionValue(value))
|
|
109
|
+
throw new TypeError('Drag value must be a motion value.')
|
|
110
|
+
if (value !== undefined && axis === 'both')
|
|
111
|
+
throw new TypeError('Dragging a motion value needs axis "x" or "y".')
|
|
86
112
|
const friction = options.friction ?? DEFAULT_FRICTION
|
|
87
113
|
if (!Number.isFinite(friction) || friction <= 0 || friction >= 1)
|
|
88
114
|
throw new RangeError('Drag friction must be between 0 and 1 exclusively.')
|
|
@@ -152,18 +178,23 @@ function settings(options: DragOptions): Settings {
|
|
|
152
178
|
const callbacks = {
|
|
153
179
|
onStart: options.onStart,
|
|
154
180
|
onMove: options.onMove,
|
|
155
|
-
onRelease: options.onRelease,
|
|
181
|
+
onRelease: options.onRelease as Settings['callbacks']['onRelease'],
|
|
156
182
|
onCancel: options.onCancel,
|
|
157
183
|
onSettle: options.onSettle,
|
|
158
184
|
}
|
|
159
185
|
for (const callback of Object.values(callbacks))
|
|
160
186
|
if (callback !== undefined && typeof callback !== 'function')
|
|
161
187
|
throw new TypeError('Drag callbacks must be functions.')
|
|
162
|
-
return { axes, curves, callbacks }
|
|
188
|
+
return { axes, curves, value: value && asRoot(value), callbacks }
|
|
163
189
|
}
|
|
164
190
|
|
|
165
|
-
/**
|
|
166
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Main-thread pan updates; release physics play through Jolt's native animation drivers.
|
|
193
|
+
* With `value`, the gesture drives a motion value and the views bound to it.
|
|
194
|
+
*/
|
|
195
|
+
export function drag(target: Target, options: DragValueOptions): Drag
|
|
196
|
+
export function drag(target: Target, options?: DragOptions): Drag
|
|
197
|
+
export function drag(target: Target, options: DragOptions | DragValueOptions = {}): Drag {
|
|
167
198
|
const view = toView(target)
|
|
168
199
|
const config = settings(options) // Invalid replacements leave the existing owner intact.
|
|
169
200
|
const previous = owners.get(view)
|
|
@@ -187,7 +218,7 @@ class DragController implements Drag {
|
|
|
187
218
|
private attached = false
|
|
188
219
|
private blocked = false
|
|
189
220
|
private generation = 0
|
|
190
|
-
private release:
|
|
221
|
+
private release: Animation | undefined
|
|
191
222
|
private input = new DragInput()
|
|
192
223
|
private origin = { x: 0, y: 0 }
|
|
193
224
|
private last: DragEvent = { x: 0, y: 0, velocityX: 0, velocityY: 0 }
|
|
@@ -215,6 +246,8 @@ class DragController implements Drag {
|
|
|
215
246
|
return this.destroyed
|
|
216
247
|
}
|
|
217
248
|
get position(): DragPoint {
|
|
249
|
+
const value = this.config.value
|
|
250
|
+
if (value) return { x: 0, y: 0, [this.config.axes[0]!]: value.base() }
|
|
218
251
|
return { x: currentValue(this.view, 'x'), y: currentValue(this.view, 'y') }
|
|
219
252
|
}
|
|
220
253
|
cancel = () => {
|
|
@@ -303,9 +336,15 @@ class DragController implements Drag {
|
|
|
303
336
|
this.cancel()
|
|
304
337
|
if (!this.attached || this.destroyed || this.disabled) return
|
|
305
338
|
this.blocked = false
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
339
|
+
const value = this.config.value
|
|
340
|
+
if (value) {
|
|
341
|
+
value.stopBase()
|
|
342
|
+
this.origin = this.position
|
|
343
|
+
} else {
|
|
344
|
+
// Claim just the enabled axes, preserving the native presentation on takeover.
|
|
345
|
+
stopBase(this.view, this.config.axes)
|
|
346
|
+
this.origin = { x: baseValue(this.view, 'x'), y: baseValue(this.view, 'y') }
|
|
347
|
+
}
|
|
309
348
|
const size =
|
|
310
349
|
(this.view.parent as View | undefined)?.getActualSize() ??
|
|
311
350
|
this.view.getActualSize()
|
|
@@ -346,8 +385,11 @@ class DragController implements Drag {
|
|
|
346
385
|
: clampDrag(raw, curve.bounds)
|
|
347
386
|
if (axis === 'x') vx = input.velocityX * resistance
|
|
348
387
|
else vy = input.velocityY * resistance
|
|
349
|
-
|
|
350
|
-
|
|
388
|
+
if (this.config.value) this.config.value.setBase(position[axis])
|
|
389
|
+
else {
|
|
390
|
+
stopBase(this.view, [axis])
|
|
391
|
+
setBase(this.view, axis, position[axis])
|
|
392
|
+
}
|
|
351
393
|
}
|
|
352
394
|
this.last = { ...position, velocityX: vx, velocityY: vy }
|
|
353
395
|
if (event.state !== 3)
|
|
@@ -383,7 +425,11 @@ class DragController implements Drag {
|
|
|
383
425
|
transition: { curve, delay: 0, repeat: 0, reverse: false },
|
|
384
426
|
}
|
|
385
427
|
})
|
|
386
|
-
const
|
|
428
|
+
const [axis] = this.config.axes
|
|
429
|
+
const value = this.config.value
|
|
430
|
+
const animation = value
|
|
431
|
+
? value.play(createTrack(axis!, [event[axis!]], plans[0]!.transition, 0))
|
|
432
|
+
: new ViewAnimation(this.view, plans)
|
|
387
433
|
this.release = animation
|
|
388
434
|
void animation.finished.then((finished) => {
|
|
389
435
|
if (generation !== this.generation || this.destroyed) return
|
package/src/driver/android.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { Application, Utils, type View } from '@nativescript/core'
|
|
13
13
|
import type { NumericProperty } from '../properties.js'
|
|
14
14
|
import type { Driver } from './index.js'
|
|
15
|
+
import { frame } from './frame.js'
|
|
15
16
|
import { isColorProperty } from '../color.js'
|
|
16
17
|
import {
|
|
17
18
|
playAndroidColor,
|
|
@@ -109,6 +110,7 @@ export function createAndroidDriver(): Driver {
|
|
|
109
110
|
|
|
110
111
|
return {
|
|
111
112
|
now: () => android.os.SystemClock.uptimeMillis(),
|
|
113
|
+
frame,
|
|
112
114
|
clock,
|
|
113
115
|
control(_view, handle, elapsed, paused, command) {
|
|
114
116
|
;(handle as Handle).control(elapsed, paused, command)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cancelAnimationFrame,
|
|
3
|
+
requestAnimationFrame,
|
|
4
|
+
} from '@nativescript/core/animation-frame'
|
|
5
|
+
|
|
6
|
+
/** NativeScript schedules subsequent frames with CADisplayLink / Choreographer. */
|
|
7
|
+
export function frame(callback: () => void): () => void {
|
|
8
|
+
const id = requestAnimationFrame(callback)
|
|
9
|
+
return () => cancelAnimationFrame(id)
|
|
10
|
+
}
|
package/src/driver/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface LayoutFrame {
|
|
|
15
15
|
export interface Driver {
|
|
16
16
|
/** Milliseconds, on the clock the platform's animations run on. */
|
|
17
17
|
now(): number
|
|
18
|
+
/** One native animation frame; returns an idempotent cancellation function. */
|
|
19
|
+
frame(callback: () => void): () => void
|
|
18
20
|
/** Starts the track at `startedAt + track.delay` and calls `onEnd` once, however it ends. */
|
|
19
21
|
play(
|
|
20
22
|
view: View,
|
package/src/driver/ios.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { playIOSColor, presentedIOSColor } from './ios-color.js'
|
|
|
13
13
|
import { after, type Driver } from './index.js'
|
|
14
14
|
import { hasInteractionSurface } from '../interaction-surface.js'
|
|
15
15
|
import { retainInteractiveLayout } from './ios-layout.js'
|
|
16
|
+
import { frame } from './frame.js'
|
|
16
17
|
|
|
17
18
|
interface Handle {
|
|
18
19
|
stop(): void
|
|
@@ -59,6 +60,7 @@ function ensurePerspective(view: View, layers: CALayer[]) {
|
|
|
59
60
|
export function createIOSDriver(): Driver {
|
|
60
61
|
return {
|
|
61
62
|
now: () => CACurrentMediaTime() * 1000,
|
|
63
|
+
frame,
|
|
62
64
|
|
|
63
65
|
play(view, track, startedAt, onEnd): Handle {
|
|
64
66
|
if (isDecorationProperty(track.property))
|
package/src/easing.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type EasingName =
|
|
|
15
15
|
| 'backOut'
|
|
16
16
|
| 'backInOut'
|
|
17
17
|
| 'anticipate'
|
|
18
|
+
| `${'quad' | 'cubic' | 'quart' | 'quint' | 'sine' | 'expo'}${'In' | 'Out' | 'InOut'}`
|
|
18
19
|
|
|
19
20
|
export type Easing = EasingName | CubicBezier | EasingFunction
|
|
20
21
|
|
|
@@ -73,6 +74,13 @@ const mirror =
|
|
|
73
74
|
(p) =>
|
|
74
75
|
p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2
|
|
75
76
|
|
|
77
|
+
const family = <N extends string>(name: N, easeIn: EasingFunction) =>
|
|
78
|
+
({
|
|
79
|
+
[`${name}In`]: easeIn,
|
|
80
|
+
[`${name}Out`]: reverse(easeIn),
|
|
81
|
+
[`${name}InOut`]: mirror(easeIn),
|
|
82
|
+
}) as Record<`${N}${'In' | 'Out' | 'InOut'}`, EasingFunction>
|
|
83
|
+
|
|
76
84
|
const easings: Record<EasingName, EasingFunction> = {
|
|
77
85
|
linear,
|
|
78
86
|
ease: cubicBezier(0.25, 0.1, 0.25, 1),
|
|
@@ -87,6 +95,12 @@ const easings: Record<EasingName, EasingFunction> = {
|
|
|
87
95
|
backInOut: mirror(backIn),
|
|
88
96
|
anticipate: (p) =>
|
|
89
97
|
p <= 0.5 ? backIn(2 * p) / 2 : p >= 1 ? 1 : 1 - Math.pow(2, 10 - 20 * p) / 2,
|
|
98
|
+
...family('quad', (p) => p ** 2),
|
|
99
|
+
...family('cubic', (p) => p ** 3),
|
|
100
|
+
...family('quart', (p) => p ** 4),
|
|
101
|
+
...family('quint', (p) => p ** 5),
|
|
102
|
+
...family('sine', (p) => (p >= 1 ? 1 : 1 - Math.cos((p * Math.PI) / 2))),
|
|
103
|
+
...family('expo', (p) => (p <= 0 ? 0 : 2 ** (10 * p - 10))),
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
export function resolveEasing(easing: Easing): EasingFunction {
|