@rafaelaugusto/jolt 0.1.0-beta.11 → 0.1.0-beta.12
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 +38 -7
- package/dist/animate.d.ts +14 -4
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +50 -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/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 +18 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +143 -36
- 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/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -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 +4 -1
- package/dist/motion-scope.d.ts.map +1 -1
- package/dist/motion-scope.js +8 -3
- 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 +115 -0
- package/dist/motion-value.d.ts.map +1 -0
- package/dist/motion-value.js +437 -0
- package/dist/motion-value.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/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/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 +1 -1
- 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 +27 -0
- package/src/animate.ts +99 -16
- package/src/drag.ts +67 -21
- package/src/easing.ts +14 -0
- package/src/engine.ts +158 -53
- package/src/gradient.ts +10 -4
- package/src/index.ts +18 -1
- package/src/keyboard.ts +83 -18
- package/src/mixer.ts +264 -0
- package/src/motion-scope.ts +12 -4
- package/src/motion-states.ts +79 -29
- package/src/motion-value.ts +563 -0
- package/src/path.ts +2 -1
- package/src/track.ts +37 -12
- package/src/transition.ts +69 -10
- package/src/types.ts +5 -0
- package/src/vue/index.ts +98 -1
package/src/transition.ts
CHANGED
|
@@ -12,10 +12,15 @@ export interface Timing {
|
|
|
12
12
|
repeat?: number
|
|
13
13
|
/** `reverse` plays every other repeat backwards. */
|
|
14
14
|
repeatType?: 'loop' | 'reverse'
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Milliseconds to hold the end value before each repeat. The first play doesn't wait.
|
|
17
|
+
* `[min, max]` draws a new pause for each repeat.
|
|
18
|
+
*/
|
|
19
|
+
repeatDelay?: number | RepeatDelayRange
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
export type RepeatDelayRange = readonly [min: number, max: number]
|
|
23
|
+
|
|
19
24
|
export interface Tween extends Timing {
|
|
20
25
|
type?: 'tween'
|
|
21
26
|
/** Milliseconds. */
|
|
@@ -107,7 +112,9 @@ export interface ResolvedTransition {
|
|
|
107
112
|
delay: number
|
|
108
113
|
repeat: number
|
|
109
114
|
reverse: boolean
|
|
110
|
-
repeatDelay?: number
|
|
115
|
+
repeatDelay?: number | RepeatDelayRange
|
|
116
|
+
/** Draws the pauses of a `repeatDelay` range; tracks of one animation share it. */
|
|
117
|
+
seed?: number
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
const springKeys = ['stiffness', 'damping', 'mass', 'bounce', 'velocity'] as const
|
|
@@ -137,11 +144,53 @@ export function timingOf(transition: Transition): Timing {
|
|
|
137
144
|
return timing
|
|
138
145
|
}
|
|
139
146
|
|
|
140
|
-
|
|
147
|
+
const isSpring = (transition: Transition) =>
|
|
148
|
+
transition.type === 'spring' ||
|
|
149
|
+
springKeys.some((key) => (transition as Spring)[key] !== undefined)
|
|
150
|
+
|
|
151
|
+
/** Curve keys a merged transition took from the main one rather than from its override. */
|
|
152
|
+
const inherited = new WeakMap<Transition, ReadonlySet<string>>()
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Timing merges key by key. Over a tween, a curve that stays a tween merges key by key too;
|
|
156
|
+
* otherwise the curve is taken whole from whichever side has one.
|
|
157
|
+
*/
|
|
141
158
|
export function mergeTransitions(base: Transition, override: Transition): Transition {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
159
|
+
if (!hasCurve(override)) {
|
|
160
|
+
const merged = { ...base, ...timingOf(override) }
|
|
161
|
+
const taken = inherited.get(base)
|
|
162
|
+
if (taken) inherited.set(merged, taken)
|
|
163
|
+
return merged
|
|
164
|
+
}
|
|
165
|
+
if (!hasCurve(base) || isSpring(base) || isSpring(override))
|
|
166
|
+
return { ...timingOf(base), ...override }
|
|
167
|
+
const merged = { ...base, ...override } as Transition
|
|
168
|
+
const own = override as Record<string, unknown>
|
|
169
|
+
inherited.set(
|
|
170
|
+
merged,
|
|
171
|
+
new Set(
|
|
172
|
+
curveKeys.filter(
|
|
173
|
+
(key) =>
|
|
174
|
+
(base as Record<string, unknown>)[key] !== undefined && own[key] === undefined,
|
|
175
|
+
),
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
return merged
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Drops inherited `times` and per-segment easings that don't fit this property's keyframes. */
|
|
182
|
+
export function fitTransition(transition: Transition, keyframes: number): Transition {
|
|
183
|
+
const taken = inherited.get(transition)
|
|
184
|
+
if (!taken) return transition
|
|
185
|
+
const tween = { ...transition } as Tween
|
|
186
|
+
const segments = (ease: Tween['ease']) =>
|
|
187
|
+
ease !== undefined && isEasingList(ease) ? ease.length : undefined
|
|
188
|
+
if (taken.has('times') && tween.times && tween.times.length !== keyframes)
|
|
189
|
+
delete tween.times
|
|
190
|
+
if (taken.has('ease') && (segments(tween.ease) ?? keyframes - 1) !== keyframes - 1)
|
|
191
|
+
delete tween.ease
|
|
192
|
+
if (taken.has('easeTrack') && segments(tween.ease) !== undefined) delete tween.easeTrack
|
|
193
|
+
return tween
|
|
145
194
|
}
|
|
146
195
|
|
|
147
196
|
export function resolveTransition(
|
|
@@ -167,13 +216,23 @@ export function resolveTransition(
|
|
|
167
216
|
if (repeat < 0 || Number.isNaN(repeat))
|
|
168
217
|
throw new RangeError(`Invalid repeat: ${repeat}.`)
|
|
169
218
|
const repeatDelay = transition.repeatDelay ?? 0
|
|
170
|
-
|
|
171
|
-
|
|
219
|
+
const [shortest, longest] =
|
|
220
|
+
typeof repeatDelay === 'number' ? [repeatDelay, repeatDelay] : repeatDelay
|
|
221
|
+
if (
|
|
222
|
+
(typeof repeatDelay !== 'number' &&
|
|
223
|
+
(!Array.isArray(repeatDelay) || repeatDelay.length !== 2)) ||
|
|
224
|
+
!Number.isFinite(shortest) ||
|
|
225
|
+
!Number.isFinite(longest) ||
|
|
226
|
+
shortest! < 0 ||
|
|
227
|
+
shortest! > longest!
|
|
228
|
+
)
|
|
229
|
+
throw new RangeError(`Invalid repeat delay: ${JSON.stringify(repeatDelay)}.`)
|
|
172
230
|
const timing = {
|
|
173
231
|
delay,
|
|
174
232
|
repeat,
|
|
175
233
|
reverse: transition.repeatType === 'reverse',
|
|
176
|
-
...(
|
|
234
|
+
...(longest! > 0 && repeat > 0 && { repeatDelay }),
|
|
235
|
+
...(longest! > shortest! && repeat > 0 && { seed: Math.random() }),
|
|
177
236
|
}
|
|
178
237
|
|
|
179
238
|
if (type === 'spring') {
|
package/src/types.ts
CHANGED
|
@@ -86,4 +86,9 @@ export type AnimateOptions = Transition & {
|
|
|
86
86
|
stagger?: number | Stagger
|
|
87
87
|
/** iOS impact feedback at arrival, or at explicit points in a finite animation. */
|
|
88
88
|
haptic?: Haptic
|
|
89
|
+
/**
|
|
90
|
+
* Plays on a named layer, on top of the property's other animations instead of replacing
|
|
91
|
+
* them. Translations and rotations add; scale and opacity multiply.
|
|
92
|
+
*/
|
|
93
|
+
layer?: string
|
|
89
94
|
}
|
package/src/vue/index.ts
CHANGED
|
@@ -15,10 +15,19 @@ import {
|
|
|
15
15
|
type Ref,
|
|
16
16
|
} from 'nativescript-vue'
|
|
17
17
|
import {
|
|
18
|
+
gradient,
|
|
18
19
|
press,
|
|
19
20
|
motionScope,
|
|
21
|
+
bind,
|
|
22
|
+
motionValue,
|
|
23
|
+
scrollValue,
|
|
20
24
|
onKeyboard,
|
|
21
25
|
onReducedMotion,
|
|
26
|
+
stop,
|
|
27
|
+
type Bindings,
|
|
28
|
+
type MotionValue,
|
|
29
|
+
type ScrollValue,
|
|
30
|
+
type Target,
|
|
22
31
|
reducedMotion,
|
|
23
32
|
type KeyboardState,
|
|
24
33
|
type MotionScope,
|
|
@@ -27,6 +36,9 @@ import {
|
|
|
27
36
|
type PlaybackAnimation,
|
|
28
37
|
type PresetName,
|
|
29
38
|
type SpringName,
|
|
39
|
+
type Gradient,
|
|
40
|
+
type GradientOptions,
|
|
41
|
+
type Transition,
|
|
30
42
|
} from '../index.js'
|
|
31
43
|
import { currentKeyboard } from '../keyboard.js'
|
|
32
44
|
|
|
@@ -37,6 +49,11 @@ type Options = AnimateOptions | SpringName
|
|
|
37
49
|
export type AnimateValue =
|
|
38
50
|
PresetName | Keyframes | readonly [animation: PresetName | Keyframes, options?: Options]
|
|
39
51
|
|
|
52
|
+
/** The gradient, alone or paired with the transition for later changes. */
|
|
53
|
+
export type GradientValue =
|
|
54
|
+
| GradientOptions
|
|
55
|
+
| readonly [gradient: GradientOptions, transition?: Transition | SpringName]
|
|
56
|
+
|
|
40
57
|
/** The pressed keyframes, alone or paired with a transition. */
|
|
41
58
|
export type PressValue = Keyframes | readonly [pressed: Keyframes, options?: Options]
|
|
42
59
|
|
|
@@ -53,6 +70,7 @@ export interface TransitionProps {
|
|
|
53
70
|
const animations = new WeakMap<object, PlaybackAnimation>()
|
|
54
71
|
const scopes = new WeakMap<object, MotionScope>()
|
|
55
72
|
const removers = new WeakMap<object, () => void>()
|
|
73
|
+
const gradients = new WeakMap<object, { gradient: Gradient; colors: number }>()
|
|
56
74
|
|
|
57
75
|
function split<T>(
|
|
58
76
|
value: T | readonly [T, Options?],
|
|
@@ -137,6 +155,47 @@ export const vPress: ObjectDirective<Host, PressValue | undefined> = {
|
|
|
137
155
|
},
|
|
138
156
|
}
|
|
139
157
|
|
|
158
|
+
function paint(el: Host, value: GradientValue, animated: boolean) {
|
|
159
|
+
const [options, transition] = (Array.isArray(value) ? value : [value]) as [
|
|
160
|
+
GradientOptions,
|
|
161
|
+
(Transition | SpringName)?,
|
|
162
|
+
]
|
|
163
|
+
const current = gradients.get(el)
|
|
164
|
+
const colors = options.colors.length
|
|
165
|
+
if (current && current.gradient.type === options.type) {
|
|
166
|
+
const { type, ...values } = options
|
|
167
|
+
if (animated && current.colors === colors)
|
|
168
|
+
current.gradient.animate(values, transition)
|
|
169
|
+
else current.gradient.set(values)
|
|
170
|
+
current.colors = colors
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
current?.gradient.remove()
|
|
174
|
+
gradients.set(el, { gradient: gradient(el, options), colors })
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Paints a native gradient behind the element, and animates to the new values when they change.
|
|
179
|
+
* A change of type, or of the number of colors, applies at once.
|
|
180
|
+
*
|
|
181
|
+
* ```vue
|
|
182
|
+
* <GridLayout v-gradient="{ type: 'linear', colors: ['#18181B', '#3F3F46'], angle: 135 }" />
|
|
183
|
+
* <GridLayout v-gradient="[{ type: 'linear', colors: active ? hot : cold }, 'snappy']" />
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export const vGradient: ObjectDirective<Host, GradientValue> = {
|
|
187
|
+
mounted(el, { value }) {
|
|
188
|
+
paint(el, value, false)
|
|
189
|
+
},
|
|
190
|
+
updated(el, { value, oldValue }) {
|
|
191
|
+
if (!same(value, oldValue)) paint(el, value, true)
|
|
192
|
+
},
|
|
193
|
+
unmounted(el) {
|
|
194
|
+
gradients.get(el)?.gradient.remove()
|
|
195
|
+
gradients.delete(el)
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
|
|
140
199
|
/** The hooks `JoltTransition` hands to Vue's `BaseTransition`. */
|
|
141
200
|
export function transitionHooks(props: TransitionProps): BaseTransitionProps<Host> {
|
|
142
201
|
const run = (value: AnimateValue | undefined) => (el: Host, done: () => void) => {
|
|
@@ -195,10 +254,11 @@ export const JoltTransition = defineComponent({
|
|
|
195
254
|
},
|
|
196
255
|
})
|
|
197
256
|
|
|
198
|
-
/** Registers `v-animate`, `v-press` and `JoltTransition` on the app. */
|
|
257
|
+
/** Registers `v-animate`, `v-press`, `v-gradient` and `JoltTransition` on the app. */
|
|
199
258
|
export function install(app: App) {
|
|
200
259
|
app.directive('animate', vAnimate)
|
|
201
260
|
app.directive('press', vPress)
|
|
261
|
+
app.directive('gradient', vGradient)
|
|
202
262
|
app.component('JoltTransition', JoltTransition)
|
|
203
263
|
}
|
|
204
264
|
|
|
@@ -229,3 +289,40 @@ export function useKeyboard(): Readonly<Ref<KeyboardState>> {
|
|
|
229
289
|
onUnmounted(off)
|
|
230
290
|
return readonly(keyboard) as Readonly<Ref<KeyboardState>>
|
|
231
291
|
}
|
|
292
|
+
|
|
293
|
+
/** Call in setup(): a motion value that stops when the component unmounts. */
|
|
294
|
+
export function useMotionValue(initial = 0): MotionValue {
|
|
295
|
+
const value = motionValue(initial)
|
|
296
|
+
onUnmounted(() => stop(value))
|
|
297
|
+
return value
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Call in setup(): a template ref that binds motion values to the element on mount
|
|
302
|
+
* and releases them on unmount. A recreated element picks the values up again.
|
|
303
|
+
*/
|
|
304
|
+
export function useBinding(bindings: Bindings): (element: Target | null) => void {
|
|
305
|
+
let element: Target | null = null
|
|
306
|
+
let release: (() => void) | undefined
|
|
307
|
+
onUnmounted(() => {
|
|
308
|
+
release?.()
|
|
309
|
+
release = undefined
|
|
310
|
+
})
|
|
311
|
+
return (next) => {
|
|
312
|
+
if (next === element) return
|
|
313
|
+
release?.()
|
|
314
|
+
release = undefined
|
|
315
|
+
element = next
|
|
316
|
+
if (next) release = bind(next, bindings)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Call in setup(): a scroll offset as a value. Pass its `ref` to the ScrollView's
|
|
322
|
+
* template ref; it stops following when the component unmounts.
|
|
323
|
+
*/
|
|
324
|
+
export function useScrollValue(options?: { axis?: 'x' | 'y' }): ScrollValue {
|
|
325
|
+
const value = scrollValue(null, options)
|
|
326
|
+
onUnmounted(() => value.dispose())
|
|
327
|
+
return value
|
|
328
|
+
}
|