@rafaelaugusto/jolt 0.1.0-beta.10 → 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 +55 -6
- 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/driver/android-gradient.d.ts +5 -0
- package/dist/driver/android-gradient.d.ts.map +1 -0
- package/dist/driver/android-gradient.js +89 -0
- package/dist/driver/android-gradient.js.map +1 -0
- package/dist/driver/ios-gradient.d.ts +5 -0
- package/dist/driver/ios-gradient.d.ts.map +1 -0
- package/dist/driver/ios-gradient.js +109 -0
- package/dist/driver/ios-gradient.js.map +1 -0
- 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 +90 -0
- package/dist/gradient.d.ts.map +1 -0
- package/dist/gradient.js +253 -0
- package/dist/gradient.js.map +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- 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 +18 -6
- package/dist/motion-states.d.ts.map +1 -1
- package/dist/motion-states.js +114 -26
- 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 +30 -4
- package/dist/transition.d.ts.map +1 -1
- package/dist/transition.js +63 -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/GradientLayer.java +195 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrame.java +53 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrameFollower.java +57 -0
- package/platforms/android/proguard-rules.pro +2 -0
- package/src/android-plugin.d.ts +56 -0
- package/src/animate.ts +99 -16
- package/src/drag.ts +67 -21
- package/src/driver/android-gradient.ts +109 -0
- package/src/driver/ios-gradient.ts +114 -0
- package/src/easing.ts +14 -0
- package/src/engine.ts +158 -53
- package/src/gradient.ts +378 -0
- package/src/index.ts +34 -3
- package/src/keyboard.ts +83 -18
- package/src/mixer.ts +264 -0
- package/src/motion-scope.ts +14 -6
- package/src/motion-states.ts +145 -41
- package/src/motion-value.ts +563 -0
- package/src/path.ts +2 -1
- package/src/track.ts +37 -12
- package/src/transition.ts +99 -10
- package/src/types.ts +5 -0
- package/src/vue/index.ts +98 -1
package/src/engine.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { createTrack, type Track } from './track.js'
|
|
|
13
13
|
import type { ResolvedTransition } from './transition.js'
|
|
14
14
|
import { isColorProperty } from './color.js'
|
|
15
15
|
import type { PreparedPath, PathProperty } from './path.js'
|
|
16
|
+
import { BASE, identity, MixHandle, mixFor, mixOf, mixesOf } from './mixer.js'
|
|
16
17
|
|
|
17
18
|
export interface Animation extends PromiseLike<boolean> {
|
|
18
19
|
/**
|
|
@@ -32,10 +33,17 @@ export interface Plan {
|
|
|
32
33
|
transition: ResolvedTransition
|
|
33
34
|
relative?: boolean
|
|
34
35
|
path?: PreparedPath
|
|
36
|
+
/** A track that is already sampled, like a motion value mapped onto a property. */
|
|
37
|
+
track?: () => Track
|
|
38
|
+
/** A named layer composed with the property's base instead of replacing it. */
|
|
39
|
+
layer?: string
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
interface Entry {
|
|
38
43
|
readonly track: Track
|
|
44
|
+
/** The property, or the property and its layer. */
|
|
45
|
+
readonly key: string
|
|
46
|
+
readonly layer?: string
|
|
39
47
|
readonly startedAt: number
|
|
40
48
|
readonly owner: ViewAnimation
|
|
41
49
|
handle: unknown
|
|
@@ -48,7 +56,7 @@ interface Entry {
|
|
|
48
56
|
|
|
49
57
|
const revisions = new WeakMap<View, Map<Property, symbol>>()
|
|
50
58
|
|
|
51
|
-
const running = new WeakMap<View, Map<
|
|
59
|
+
const running = new WeakMap<View, Map<string, Entry>>()
|
|
52
60
|
const arrivals = new WeakMap<Animation, Promise<unknown>>()
|
|
53
61
|
|
|
54
62
|
/** Internal forecast for starting a timeline step before another one arrives. */
|
|
@@ -188,7 +196,7 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
188
196
|
entry.done = false
|
|
189
197
|
entry.finished = false
|
|
190
198
|
this.pending++
|
|
191
|
-
entriesOf(this.view).set(entry.
|
|
199
|
+
entriesOf(this.view).set(entry.key, entry)
|
|
192
200
|
this.playEntry(entry, position)
|
|
193
201
|
}
|
|
194
202
|
}
|
|
@@ -199,9 +207,9 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
199
207
|
this.timingChanged()
|
|
200
208
|
}
|
|
201
209
|
elapsed(entry: Entry, now: number) {
|
|
202
|
-
return this.
|
|
203
|
-
|
|
204
|
-
|
|
210
|
+
if (this.controlled) return this.time()
|
|
211
|
+
if (entry.handle instanceof MixHandle) return entry.handle.elapsed()
|
|
212
|
+
return getDriver().elapsed?.(entry.handle) ?? now - entry.startedAt
|
|
205
213
|
}
|
|
206
214
|
private time() {
|
|
207
215
|
return this.clock?.time() ?? 0
|
|
@@ -209,8 +217,10 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
209
217
|
private controlTracks(position: number, command: PlaybackCommand = 'seek') {
|
|
210
218
|
const driver = getDriver()
|
|
211
219
|
for (const entry of this.entries)
|
|
212
|
-
if (
|
|
213
|
-
|
|
220
|
+
if (entry.done) continue
|
|
221
|
+
else if (entry.handle instanceof MixHandle)
|
|
222
|
+
entry.handle.control(position, this._paused)
|
|
223
|
+
else driver.control?.(this.view, entry.handle, position, this._paused, command)
|
|
214
224
|
}
|
|
215
225
|
private scheduleArrival() {
|
|
216
226
|
this.cancelArrival?.()
|
|
@@ -244,19 +254,31 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
244
254
|
const initial = entry.generation === 0
|
|
245
255
|
const instance = ++entry.generation
|
|
246
256
|
const driver = getDriver()
|
|
247
|
-
entry.
|
|
248
|
-
|
|
249
|
-
entry.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
const startedAt = initial ? entry.startedAt : driver.now() - elapsed
|
|
258
|
+
const onEnd = (finished = true) => {
|
|
259
|
+
if (entry.done || instance !== entry.generation) return
|
|
260
|
+
entry.done = true
|
|
261
|
+
entry.finished = finished
|
|
262
|
+
entry.completedValue = readValue(this.view, entry.track.property)
|
|
263
|
+
forget(entry)
|
|
264
|
+
this.ended(finished)
|
|
265
|
+
}
|
|
266
|
+
const { property } = entry.track
|
|
267
|
+
const mix =
|
|
268
|
+
entry.layer !== undefined ? mixing(this.view, property) : mixOf(this.view, property)
|
|
269
|
+
entry.handle = mix
|
|
270
|
+
? mix.adopt(entry.layer ?? BASE, entry.track, startedAt, onEnd)
|
|
271
|
+
: driver.play(this.view, entry.track, startedAt, onEnd)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Moves a natively playing entry into its property's mix, without restarting it. */
|
|
275
|
+
rehome(entry: Entry) {
|
|
276
|
+
if (entry.done || entry.handle instanceof MixHandle) return
|
|
277
|
+
const elapsed = this.elapsed(entry, getDriver().now())
|
|
278
|
+
const handle = entry.handle
|
|
279
|
+
entry.generation++
|
|
280
|
+
getDriver().stop(this.view, handle)
|
|
281
|
+
this.playEntry(entry, elapsed)
|
|
260
282
|
}
|
|
261
283
|
private unloaded = () => this.stop()
|
|
262
284
|
|
|
@@ -322,52 +344,78 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
322
344
|
const now = driver.now()
|
|
323
345
|
const entries = entriesOf(this.view)
|
|
324
346
|
|
|
325
|
-
const
|
|
326
|
-
const
|
|
347
|
+
const made = plans.map((plan) => {
|
|
348
|
+
const { property, keyframes, transition, relative, path, layer } = plan
|
|
349
|
+
const key = layer === undefined ? property : `${property}#${layer}`
|
|
350
|
+
const mixed = layer !== undefined || mixOf(this.view, property) !== undefined
|
|
351
|
+
const previous = entries.get(key)
|
|
327
352
|
let current: number
|
|
328
353
|
let velocity = 0
|
|
329
354
|
if (previous) {
|
|
330
355
|
const elapsed = elapsedOf(previous, now)
|
|
331
|
-
current =
|
|
356
|
+
current =
|
|
357
|
+
(mixed ? undefined : driver.presented(this.view, property)) ??
|
|
358
|
+
previous.track.valueAt(elapsed)
|
|
332
359
|
velocity = previous.owner.paused ? 0 : previous.track.velocityAt(elapsed)
|
|
333
360
|
release(previous, now, false)
|
|
361
|
+
} else if (mixed) {
|
|
362
|
+
current =
|
|
363
|
+
mixOf(this.view, property)?.rest(layer ?? BASE) ??
|
|
364
|
+
(layer === undefined ? readValue(this.view, property) : identity(property))
|
|
334
365
|
} else {
|
|
335
366
|
current =
|
|
336
367
|
(isColorProperty(property)
|
|
337
368
|
? driver.presented(this.view, property)
|
|
338
369
|
: undefined) ?? readValue(this.view, property)
|
|
339
370
|
}
|
|
340
|
-
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
371
|
+
const build = () => {
|
|
372
|
+
if (plan.track) return plan.track()
|
|
373
|
+
if (path)
|
|
374
|
+
return path.track(
|
|
375
|
+
property as PathProperty,
|
|
376
|
+
relative ? current : 0,
|
|
377
|
+
transition.delay,
|
|
378
|
+
)
|
|
379
|
+
return createTrack(
|
|
380
|
+
property,
|
|
381
|
+
keyframes.map((value) => {
|
|
382
|
+
if (value === null) return current
|
|
383
|
+
if (layer !== undefined || !relative || isColorProperty(property))
|
|
384
|
+
return value
|
|
385
|
+
return property === 'opacity' || property.startsWith('scale')
|
|
386
|
+
? current * value
|
|
387
|
+
: current + value
|
|
388
|
+
}),
|
|
389
|
+
transition,
|
|
390
|
+
velocity,
|
|
345
391
|
)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
keyframes.map((value) => {
|
|
349
|
-
if (value === null) return current
|
|
350
|
-
if (!relative || isColorProperty(property)) return value
|
|
351
|
-
return property === 'opacity' || property.startsWith('scale')
|
|
352
|
-
? current * value
|
|
353
|
-
: current + value
|
|
354
|
-
}),
|
|
355
|
-
transition,
|
|
356
|
-
velocity,
|
|
357
|
-
)
|
|
392
|
+
}
|
|
393
|
+
return { track: build(), key, layer }
|
|
358
394
|
})
|
|
359
|
-
|
|
360
|
-
|
|
395
|
+
const tracks = made.map(({ track }) => track)
|
|
396
|
+
|
|
397
|
+
for (const { track, layer } of made) {
|
|
398
|
+
const mix =
|
|
399
|
+
layer !== undefined
|
|
400
|
+
? mixing(this.view, track.property)
|
|
401
|
+
: mixOf(this.view, track.property)
|
|
402
|
+
if (!mix) writeValue(this.view, track.property, track.to)
|
|
403
|
+
else if (track.end === 0) mix.setRest(layer ?? BASE, track.to)
|
|
404
|
+
}
|
|
361
405
|
driver.sync?.(this.view)
|
|
362
406
|
|
|
363
|
-
for (const track of
|
|
364
|
-
let tokens = revisions.get(this.view)
|
|
365
|
-
if (!tokens) revisions.set(this.view, (tokens = new Map()))
|
|
407
|
+
for (const { track, key, layer } of made) {
|
|
366
408
|
const token = Symbol(track.property)
|
|
367
|
-
|
|
409
|
+
if (layer === undefined) {
|
|
410
|
+
let tokens = revisions.get(this.view)
|
|
411
|
+
if (!tokens) revisions.set(this.view, (tokens = new Map()))
|
|
412
|
+
tokens.set(track.property, token)
|
|
413
|
+
}
|
|
368
414
|
if (track.end === 0) continue
|
|
369
415
|
const entry: Entry = {
|
|
370
416
|
track,
|
|
417
|
+
key,
|
|
418
|
+
layer,
|
|
371
419
|
startedAt: now,
|
|
372
420
|
owner: this,
|
|
373
421
|
handle: undefined,
|
|
@@ -376,7 +424,7 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
376
424
|
generation: 0,
|
|
377
425
|
token,
|
|
378
426
|
}
|
|
379
|
-
entries.set(
|
|
427
|
+
entries.set(key, entry)
|
|
380
428
|
this.entries.push(entry)
|
|
381
429
|
this.pending++
|
|
382
430
|
}
|
|
@@ -405,8 +453,8 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
405
453
|
}
|
|
406
454
|
|
|
407
455
|
private waitForLoad(plans: readonly Plan[]) {
|
|
408
|
-
for (const { property, keyframes, relative } of plans) {
|
|
409
|
-
if (relative && !isColorProperty(property)) continue
|
|
456
|
+
for (const { property, keyframes, relative, layer } of plans) {
|
|
457
|
+
if (layer !== undefined || (relative && !isColorProperty(property))) continue
|
|
410
458
|
const first = keyframes[0]
|
|
411
459
|
if (first !== null && first !== undefined) writeValue(this.view, property, first)
|
|
412
460
|
}
|
|
@@ -438,6 +486,8 @@ export class ViewAnimation implements PlaybackAnimation {
|
|
|
438
486
|
|
|
439
487
|
/** Current native presentation when available, otherwise the active curve or model. */
|
|
440
488
|
export function currentValue(view: View, property: Property): number {
|
|
489
|
+
const mix = mixOf(view, property)
|
|
490
|
+
if (mix) return mix.value()
|
|
441
491
|
const entry = running.get(view)?.get(property)
|
|
442
492
|
if (!entry) return readValue(view, property)
|
|
443
493
|
const driver = getDriver()
|
|
@@ -465,19 +515,69 @@ export function captureValue(view: View, property: Property): () => number | und
|
|
|
465
515
|
}
|
|
466
516
|
}
|
|
467
517
|
|
|
468
|
-
/** Stops whatever is animating these properties of the view, or all of them. */
|
|
518
|
+
/** Stops whatever is animating these properties of the view, or all of them, layers included. */
|
|
469
519
|
export function stopView(view: View, properties?: readonly Property[]) {
|
|
470
520
|
const tokens = revisions.get(view)
|
|
471
521
|
for (const property of properties ?? tokens?.keys() ?? [])
|
|
472
522
|
tokens?.set(property, Symbol(property))
|
|
523
|
+
for (const mix of mixesOf(view))
|
|
524
|
+
if (!properties || properties.includes(mix.property)) mix.freeze()
|
|
473
525
|
const entries = running.get(view)
|
|
474
526
|
if (!entries) return
|
|
475
527
|
const now = getDriver().now()
|
|
476
|
-
for (const
|
|
477
|
-
if (!properties || properties.includes(property))
|
|
528
|
+
for (const entry of [...entries.values()]) {
|
|
529
|
+
if (!properties || properties.includes(entry.track.property))
|
|
530
|
+
release(entry, now, true)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** Stops the regular animations of these properties and leaves their layers running. */
|
|
535
|
+
export function stopBase(view: View, properties: readonly Property[]) {
|
|
536
|
+
const tokens = revisions.get(view)
|
|
537
|
+
for (const property of properties) tokens?.set(property, Symbol(property))
|
|
538
|
+
const now = getDriver().now()
|
|
539
|
+
for (const property of properties) {
|
|
540
|
+
const entry = running.get(view)?.get(property)
|
|
541
|
+
if (entry) release(entry, now, true)
|
|
478
542
|
}
|
|
479
543
|
}
|
|
480
544
|
|
|
545
|
+
/** Stops one layer and takes away what it adds, also after it finished. */
|
|
546
|
+
export function stopLayer(view: View, layer: string, properties?: readonly Property[]) {
|
|
547
|
+
const now = getDriver().now()
|
|
548
|
+
for (const entry of [...(running.get(view)?.values() ?? [])])
|
|
549
|
+
if (
|
|
550
|
+
entry.layer === layer &&
|
|
551
|
+
(!properties || properties.includes(entry.track.property))
|
|
552
|
+
)
|
|
553
|
+
release(entry, now, true)
|
|
554
|
+
for (const mix of mixesOf(view))
|
|
555
|
+
if (!properties || properties.includes(mix.property))
|
|
556
|
+
mix.setRest(layer, identity(mix.property))
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** Writes a property's base value, under its layers when it has any. */
|
|
560
|
+
export function setBase(view: View, property: Property, value: number) {
|
|
561
|
+
const mix = mixOf(view, property)
|
|
562
|
+
if (mix) mix.setRest(BASE, value)
|
|
563
|
+
else writeValue(view, property, value)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/** A property's base value, without what its layers add. */
|
|
567
|
+
export function baseValue(view: View, property: Property): number {
|
|
568
|
+
return mixOf(view, property)?.rest(BASE) ?? readValue(view, property)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/** The property's mix, created on its first layer; a base playing natively moves into it. */
|
|
572
|
+
function mixing(view: View, property: Property) {
|
|
573
|
+
const existing = mixOf(view, property)
|
|
574
|
+
if (existing) return existing
|
|
575
|
+
const mix = mixFor(view, property)
|
|
576
|
+
const base = running.get(view)?.get(property)
|
|
577
|
+
if (base) base.owner.rehome(base)
|
|
578
|
+
return mix
|
|
579
|
+
}
|
|
580
|
+
|
|
481
581
|
function entriesOf(view: View) {
|
|
482
582
|
let entries = running.get(view)
|
|
483
583
|
if (!entries) running.set(view, (entries = new Map()))
|
|
@@ -486,7 +586,7 @@ function entriesOf(view: View) {
|
|
|
486
586
|
|
|
487
587
|
function forget(entry: Entry) {
|
|
488
588
|
const entries = running.get(entry.owner.view)
|
|
489
|
-
if (entries?.get(entry.
|
|
589
|
+
if (entries?.get(entry.key) === entry) entries.delete(entry.key)
|
|
490
590
|
}
|
|
491
591
|
|
|
492
592
|
function release(entry: Entry, now: number, commit: boolean) {
|
|
@@ -495,6 +595,11 @@ function release(entry: Entry, now: number, commit: boolean) {
|
|
|
495
595
|
const { property } = entry.track
|
|
496
596
|
entry.done = true
|
|
497
597
|
forget(entry)
|
|
598
|
+
if (entry.handle instanceof MixHandle) {
|
|
599
|
+
entry.handle.mix.detach(entry.layer ?? BASE, commit)
|
|
600
|
+
entry.owner.ended(false)
|
|
601
|
+
return
|
|
602
|
+
}
|
|
498
603
|
if (commit) {
|
|
499
604
|
const value =
|
|
500
605
|
driver.presented(view, property) ?? entry.track.valueAt(elapsedOf(entry, now))
|