@xtia/timeline 1.1.2 → 1.1.4
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 +67 -33
- package/internal/emitters.d.ts +2 -2
- package/internal/emitters.js +13 -12
- package/internal/point.d.ts +20 -3
- package/internal/point.js +30 -14
- package/internal/range.d.ts +3 -2
- package/internal/range.js +12 -25
- package/internal/timeline.d.ts +21 -6
- package/internal/timeline.js +52 -21
- package/internal/tween.d.ts +1 -1
- package/internal/tween.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Timeline is a type-safe, seekable, deterministic choreography system that can co
|
|
|
6
6
|
|
|
7
7
|
* [API Reference](#reference)
|
|
8
8
|
* [Playground](https://stackblitz.com/edit/timeline-string-tween?file=src%2Fmain.ts)
|
|
9
|
+
* [Intro by HL](https://codepen.io/H-L-the-lessful/full/vELdyvB)
|
|
9
10
|
|
|
10
11
|
## Basic Use:
|
|
11
12
|
|
|
@@ -40,7 +41,7 @@ timeline
|
|
|
40
41
|
timeline
|
|
41
42
|
.range(1000, 2000)
|
|
42
43
|
.tween(0, 255)
|
|
43
|
-
.
|
|
44
|
+
.apply(value => microcontroller.setPWM(value))
|
|
44
45
|
|
|
45
46
|
// make it go
|
|
46
47
|
timeline.play();
|
|
@@ -113,13 +114,11 @@ const urlEmitter = filenameEmitter
|
|
|
113
114
|
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
Range objects also
|
|
117
|
+
Range objects also be passed to `Timeline`'s `play()` method to play through that particular range:
|
|
117
118
|
|
|
118
119
|
```ts
|
|
119
|
-
// play through the first
|
|
120
|
-
await timeline
|
|
121
|
-
.range(0, 2000)
|
|
122
|
-
.play();
|
|
120
|
+
// play through the first 5 seconds of the Timeline at 1000 units/s
|
|
121
|
+
await timeline.play(firstFiveSeconds);
|
|
123
122
|
```
|
|
124
123
|
|
|
125
124
|
Custom easers can be passed to `ease()` as `(progress: number) => number`:
|
|
@@ -356,6 +355,27 @@ Despite the massive overhaul, the previous API is present and expanded and upgra
|
|
|
356
355
|
|
|
357
356
|
## Reference
|
|
358
357
|
|
|
358
|
+
### Contents
|
|
359
|
+
|
|
360
|
+
#### Functions
|
|
361
|
+
|
|
362
|
+
* [`animate`](#animateduration-function)
|
|
363
|
+
|
|
364
|
+
#### Classes
|
|
365
|
+
|
|
366
|
+
* [`Timeline`](#timeline-class)
|
|
367
|
+
* [`TimelinePoint`](#timelinepoint-class)
|
|
368
|
+
* [`TimelineRange`](#timelinerange-class)
|
|
369
|
+
* [`RangeProgression`](#rangeprogression-class)
|
|
370
|
+
* [`Emitter<T>`](#emittert-class)
|
|
371
|
+
|
|
372
|
+
#### Interfaces
|
|
373
|
+
|
|
374
|
+
* [`PointEvent`](#pointevent-interface)
|
|
375
|
+
* [`ChainingInterface`](#chaininginterface-interface)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
359
379
|
### `Timeline` class
|
|
360
380
|
|
|
361
381
|
A self-contained collection of points and ranges that trigger events as the Timeline seeks to and through them.
|
|
@@ -374,17 +394,17 @@ Controls the speed at which a Timeline will progress when driven by the `play()`
|
|
|
374
394
|
|
|
375
395
|
Returns true if the Timeline is actively being driven by the `play()` method (including by autoplay).
|
|
376
396
|
|
|
377
|
-
##### `end: `[`TimelinePoint`](#timelinepoint-
|
|
397
|
+
##### `end: `[`TimelinePoint`](#timelinepoint-class)
|
|
378
398
|
|
|
379
399
|
Returns the **current** final point in the Timeline.
|
|
380
400
|
|
|
381
|
-
##### `start: `[`TimelinePoint`](#timelinepoint-
|
|
401
|
+
##### `start: `[`TimelinePoint`](#timelinepoint-class)
|
|
382
402
|
|
|
383
403
|
Returns a point representing position 0.
|
|
384
404
|
|
|
385
405
|
#### Methods
|
|
386
406
|
|
|
387
|
-
##### `point(position): `[`TimelinePoint`](#timelinepoint-
|
|
407
|
+
##### `point(position): `[`TimelinePoint`](#timelinepoint-class)
|
|
388
408
|
|
|
389
409
|
Returns a point that represents a specific position on the Timeline.
|
|
390
410
|
|
|
@@ -392,7 +412,7 @@ If `position` is greater than that Timeline's end-position, the end-position wil
|
|
|
392
412
|
|
|
393
413
|
*Note*, for deterministic consistency, points will be triggered if a forward-moving seek lands exactly on the point's position (or passes it entirely), while a backward-moving seek will trigger points that are passed or moved from.
|
|
394
414
|
|
|
395
|
-
##### `range(start, duration): `[`TimelineRange`](#timelinerange-
|
|
415
|
+
##### `range(start, duration): `[`TimelineRange`](#timelinerange-class)
|
|
396
416
|
|
|
397
417
|
Returns a range that represents a section of the Timeline.
|
|
398
418
|
|
|
@@ -404,7 +424,9 @@ If `start` is omitted, the range will start at 0 and represent the full **curren
|
|
|
404
424
|
|
|
405
425
|
##### `seek(toPosition): void`
|
|
406
426
|
|
|
407
|
-
Sets the Timeline's internal position (`currentTime`), triggering in chronological order listeners attached to any [`TimelinePoint`](#timelinepoint-
|
|
427
|
+
Sets the Timeline's internal position (`currentTime`), triggering in chronological order listeners attached to any [`TimelinePoint`](#timelinepoint-class) or [`TimelineRange`](#timelinerange-class) that are passed or landed on.
|
|
428
|
+
|
|
429
|
+
`toPosition` may be a number or a [`TimelinePoint`](#timelinepoint-class).
|
|
408
430
|
|
|
409
431
|
##### `seek(toPosition, duration, easer?): Promise<void>`
|
|
410
432
|
|
|
@@ -422,9 +444,13 @@ Begins playing through the Timeline, from its current position, at (1000 × `tim
|
|
|
422
444
|
|
|
423
445
|
Begins playing through the Timeline, from its current position, at (1000 × `timeScale`) units per second, updating `fps` times per second.
|
|
424
446
|
|
|
447
|
+
##### `play(range, easer?): Promise<void>`
|
|
448
|
+
|
|
449
|
+
If a [`TimelineRange`](#timelinerange-class) is passed, the Timeline will play through that range at 1000 units per second, following the rules of a [smooth seek](#seektoposition-duration-easer-promisevoid).
|
|
450
|
+
|
|
425
451
|
##### `tween<T>(start, duration, apply, from, to, easer?): `[`ChainingInterface`](#chaininginterface-interface)
|
|
426
452
|
|
|
427
|
-
Creates a [`TimelineRange`](#timelinerange-
|
|
453
|
+
Creates a [`TimelineRange`](#timelinerange-class) and attaches a tweening listener.
|
|
428
454
|
|
|
429
455
|
Equivalent to
|
|
430
456
|
|
|
@@ -440,22 +466,24 @@ Returns a [`ChainingInterface`](#chaininginterface-interface) representing the p
|
|
|
440
466
|
|
|
441
467
|
##### `tween<T>(start, end, apply, from, to, easer?): `[`ChainingInterface`](#chaininginterface-interface)
|
|
442
468
|
|
|
443
|
-
As above, but if the second argument is a [`TimelinePoint`](#timelinepoint-
|
|
469
|
+
As above, but if the second argument is a [`TimelinePoint`](#timelinepoint-class), it will specify when on the Timeline the tween will *end*.
|
|
444
470
|
|
|
445
471
|
##### `at(position, apply, reverse?): `[`ChainingInterface`](#chaininginterface-interface)
|
|
446
472
|
|
|
447
|
-
Creates a [`TimelinePoint`](#timelinepoint-
|
|
473
|
+
Creates a [`TimelinePoint`](#timelinepoint-class) and attaches a listener that will trigger when the Timeline seeks past or to that point.
|
|
448
474
|
|
|
449
475
|
If `reverse` is a function, that will be called instead of `apply` when the seek that triggered the event was moving backwards. If `reverse` is `true`, `apply` will be called regardless of which direction the seek moved. If `reverse` is false or omitted, this listener will ignore backward-moving seeks.
|
|
450
476
|
|
|
451
477
|
|
|
452
478
|
|
|
453
479
|
|
|
454
|
-
### `TimelinePoint`
|
|
480
|
+
### `TimelinePoint` class
|
|
455
481
|
|
|
456
482
|
Represents a single point on a [`Timeline`](#timeline-class).
|
|
457
483
|
|
|
458
|
-
|
|
484
|
+
This class is not meant to be constructed directly; instances are created with [`Timeline.point()`](#pointposition-timelinepoint).
|
|
485
|
+
|
|
486
|
+
##### Inherits [`Emitter<PointEvent>`](#emittert-class)
|
|
459
487
|
|
|
460
488
|
Listeners will be invoked with a [`PointEvent`](#pointevent-interface) when a seek passes or lands on the point.
|
|
461
489
|
|
|
@@ -471,11 +499,11 @@ This point's position on the Timeline.
|
|
|
471
499
|
|
|
472
500
|
##### `range(duration): TimelineRange`
|
|
473
501
|
|
|
474
|
-
Creates a [`TimelineRange`](#timelinerange-
|
|
502
|
+
Creates a [`TimelineRange`](#timelinerange-class) on the Timeline to which the point belongs, of the specified duration.
|
|
475
503
|
|
|
476
504
|
##### `to(endPoint): TimelineRange`
|
|
477
505
|
|
|
478
|
-
Creates a [`TimelineRange`](#timelinerange-
|
|
506
|
+
Creates a [`TimelineRange`](#timelinerange-class) on the Timeline to which the point belongs, ending at the specified point.
|
|
479
507
|
|
|
480
508
|
##### `delta(timeOffset): TimelinePoint`
|
|
481
509
|
|
|
@@ -520,7 +548,7 @@ point
|
|
|
520
548
|
|
|
521
549
|
### `PointEvent` interface
|
|
522
550
|
|
|
523
|
-
Provides information relevant to [`TimelinePoint`](#timelinepoint-
|
|
551
|
+
Provides information relevant to [`TimelinePoint`](#timelinepoint-class) events.
|
|
524
552
|
|
|
525
553
|
#### Properties
|
|
526
554
|
|
|
@@ -544,21 +572,23 @@ timeline
|
|
|
544
572
|
|
|
545
573
|
|
|
546
574
|
|
|
547
|
-
### `TimelineRange`
|
|
575
|
+
### `TimelineRange` class
|
|
548
576
|
|
|
549
577
|
Represents a fixed-length, fixed position section of a [`Timeline`](#timeline-class).
|
|
550
578
|
|
|
551
|
-
|
|
579
|
+
This class is not meant to be constructed directly; instances are created with [`Timeline.range()`](#rangestart-duration-timelinerange).
|
|
580
|
+
|
|
581
|
+
##### Inherits [`RangeProgression`](#rangeprogression-class)
|
|
552
582
|
|
|
553
583
|
Emits a normalised progression (0..1) of the range when the parent Timeline seeks over or into it.
|
|
554
584
|
|
|
555
585
|
#### Properties
|
|
556
586
|
|
|
557
|
-
##### `start: `[`TimelinePoint`](#timelinepoint-
|
|
587
|
+
##### `start: `[`TimelinePoint`](#timelinepoint-class)
|
|
558
588
|
|
|
559
589
|
The point on the Timeline at which this range starts.
|
|
560
590
|
|
|
561
|
-
##### `end: `[`TimelinePoint`](#timelinepoint-
|
|
591
|
+
##### `end: `[`TimelinePoint`](#timelinepoint-class)
|
|
562
592
|
|
|
563
593
|
The point on the Timeline at which this range ends.
|
|
564
594
|
|
|
@@ -572,7 +602,7 @@ The length of the range.
|
|
|
572
602
|
|
|
573
603
|
Creates two ranges representing two distinct sections of the parent. `position` is relative to the parent's start.
|
|
574
604
|
|
|
575
|
-
##### `spread(count): `[`TimelinePoint`](#timelinepoint-
|
|
605
|
+
##### `spread(count): `[`TimelinePoint`](#timelinepoint-class)[]
|
|
576
606
|
|
|
577
607
|
Creates and returns `count` points spread evenly over the range.
|
|
578
608
|
|
|
@@ -600,7 +630,7 @@ Creates a new range by offsetting the parent by a given time delta.
|
|
|
600
630
|
|
|
601
631
|
##### `contains(point): boolean`
|
|
602
632
|
|
|
603
|
-
Returns true if the given [`TimelinePoint`](#timelinepoint-
|
|
633
|
+
Returns true if the given [`TimelinePoint`](#timelinepoint-class) sits within this range.
|
|
604
634
|
|
|
605
635
|
##### `overlaps(range): boolean`
|
|
606
636
|
|
|
@@ -609,11 +639,13 @@ Returns true if the given range overlaps with this range.
|
|
|
609
639
|
|
|
610
640
|
|
|
611
641
|
|
|
612
|
-
### `RangeProgression`
|
|
642
|
+
### `RangeProgression` class
|
|
613
643
|
|
|
614
|
-
Represents a step in an immutable [`TimelineRange`](#timelinerange-
|
|
644
|
+
Represents a step in an immutable [`TimelineRange`](#timelinerange-class) event transformation pipeline.
|
|
615
645
|
|
|
616
|
-
|
|
646
|
+
This class is not meant to be constructed directly; instances are created by various transformation methods of [`TimelineRange`](#timelinerange-class).
|
|
647
|
+
|
|
648
|
+
##### Inherits [`Emitter<number>`](#emittert-class)
|
|
617
649
|
|
|
618
650
|
Listeners will be invoked when a seek passes or lands within a range.
|
|
619
651
|
|
|
@@ -623,7 +655,7 @@ Listeners will be invoked when a seek passes or lands within a range.
|
|
|
623
655
|
|
|
624
656
|
Creates an emitter that applies an easing function to parent emissions.
|
|
625
657
|
|
|
626
|
-
##### `tween<T>(from, to): `[`Emitter<T>`](#emittert-
|
|
658
|
+
##### `tween<T>(from, to): `[`Emitter<T>`](#emittert-class)
|
|
627
659
|
|
|
628
660
|
Creates an emitter blends two values, biased by progression emitted by the parent.
|
|
629
661
|
|
|
@@ -637,7 +669,7 @@ blend(from: this, to: this, progress: number): this
|
|
|
637
669
|
|
|
638
670
|
Creates an emitter that quantises progression emitted by the parent to the nearest of `steps` discrete values.
|
|
639
671
|
|
|
640
|
-
##### `sample<T>(values: ArrayLike<T>): `[`Emitter<T>`](#emittert-
|
|
672
|
+
##### `sample<T>(values: ArrayLike<T>): `[`Emitter<T>`](#emittert-class)
|
|
641
673
|
|
|
642
674
|
Creates an emitter that emits values from an array according to progression.
|
|
643
675
|
|
|
@@ -705,7 +737,7 @@ range
|
|
|
705
737
|
|
|
706
738
|
|
|
707
739
|
|
|
708
|
-
### `Emitter<T>`
|
|
740
|
+
### `Emitter<T>` class
|
|
709
741
|
|
|
710
742
|
#### Methods
|
|
711
743
|
|
|
@@ -713,6 +745,8 @@ range
|
|
|
713
745
|
|
|
714
746
|
Attaches a handler to the emitter and returns a function that will unsubscribe the handler.
|
|
715
747
|
|
|
748
|
+
This class is not meant to be constructed directly; instances are created by transformation methods.
|
|
749
|
+
|
|
716
750
|
##### `map<R>(mapFunc: (value: T) => R): Emitter<R>`
|
|
717
751
|
|
|
718
752
|
Creates an emitter that performs an arbitrary transformation.
|
|
@@ -753,7 +787,7 @@ range
|
|
|
753
787
|
|
|
754
788
|
### `animate(duration)` function
|
|
755
789
|
|
|
756
|
-
Creates and returns a [`TimelineRange`](#timelinerange-
|
|
790
|
+
Creates and returns a [`TimelineRange`](#timelinerange-class) that will automatically play over `duration` milliseconds.
|
|
757
791
|
|
|
758
792
|
### `ChainingInterface` interface
|
|
759
793
|
|
|
@@ -770,7 +804,7 @@ timeline
|
|
|
770
804
|
|
|
771
805
|
#### Properties
|
|
772
806
|
|
|
773
|
-
##### `end: `[`TimelinePoint`](#timelinepoint-
|
|
807
|
+
##### `end: `[`TimelinePoint`](#timelinepoint-class)
|
|
774
808
|
|
|
775
809
|
The point on the Timeline at which the effect of the previous chained call ends.
|
|
776
810
|
|
package/internal/emitters.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare class Emitter<T> {
|
|
|
14
14
|
* @param listen
|
|
15
15
|
* @returns {this}
|
|
16
16
|
*/
|
|
17
|
-
protected redirect
|
|
17
|
+
protected redirect(listen: ListenFunc<T>): Emitter<T>;
|
|
18
18
|
/**
|
|
19
19
|
* Compatibility alias for `apply()` - registers a function to receive emitted values
|
|
20
20
|
* @param handler
|
|
@@ -80,7 +80,7 @@ export declare class Emitter<T> {
|
|
|
80
80
|
fork(cb: (branch: this) => void): this;
|
|
81
81
|
}
|
|
82
82
|
export declare class RangeProgression extends Emitter<number> {
|
|
83
|
-
protected redirect
|
|
83
|
+
protected redirect(listen: ListenFunc<number>): RangeProgression;
|
|
84
84
|
/**
|
|
85
85
|
* Creates a chainable progress emitter that applies an easing function to its parent's emitted values
|
|
86
86
|
*
|
package/internal/emitters.js
CHANGED
|
@@ -4,15 +4,17 @@ import { clamp } from "./utils";
|
|
|
4
4
|
export class Emitter {
|
|
5
5
|
constructor(onListen) {
|
|
6
6
|
this.onListen = onListen;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Used by tap() to create a clone of an Emitter with a redirected onListen
|
|
10
|
+
*
|
|
11
|
+
* Should be overridden in all Emitter subclasses
|
|
12
|
+
* @see {@link TimelineRange.redirect}
|
|
13
|
+
* @param listen
|
|
14
|
+
* @returns {this}
|
|
15
|
+
*/
|
|
16
|
+
redirect(listen) {
|
|
17
|
+
return new Emitter(listen);
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Compatibility alias for `apply()` - registers a function to receive emitted values
|
|
@@ -133,9 +135,8 @@ export class Emitter {
|
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
export class RangeProgression extends Emitter {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.redirect = (listen) => new RangeProgression(listen);
|
|
138
|
+
redirect(listen) {
|
|
139
|
+
return new RangeProgression(listen);
|
|
139
140
|
}
|
|
140
141
|
ease(easer) {
|
|
141
142
|
if (!easer)
|
package/internal/point.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare class TimelinePoint extends Emitter<PointEvent> {
|
|
|
17
17
|
* The point's absolute position on the Timeline
|
|
18
18
|
*/
|
|
19
19
|
position: number);
|
|
20
|
-
protected redirect
|
|
20
|
+
protected redirect(listen: ListenFunc<PointEvent>): TimelinePoint;
|
|
21
21
|
/**
|
|
22
22
|
* Creates a range on the Timeline, with a given duration, starting at this point
|
|
23
23
|
* @param duration
|
|
@@ -38,19 +38,31 @@ export declare class TimelinePoint extends Emitter<PointEvent> {
|
|
|
38
38
|
delta(timeOffset: number): TimelinePoint;
|
|
39
39
|
/**
|
|
40
40
|
* Seeks the parent Timeline to this point
|
|
41
|
+
* @deprecated Use timeline.seek(point)
|
|
41
42
|
*/
|
|
42
43
|
seek(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Smooth-seeks the parent Timeline to this point
|
|
46
|
+
* @deprecated Use timeline.seek(point)
|
|
47
|
+
*/
|
|
43
48
|
seek(duration: number, easer?: Easer): Promise<void>;
|
|
44
49
|
/**
|
|
45
50
|
* Creates an emitter that only emits on forward-moving seeks
|
|
46
|
-
* @returns
|
|
51
|
+
* @returns Listenable: emits forward-seeking point events
|
|
47
52
|
*/
|
|
48
53
|
forwardOnly(): Emitter<PointEvent>;
|
|
49
54
|
/**
|
|
50
55
|
* Creates an emitter that only emits on backward-moving seeks
|
|
51
|
-
* @returns
|
|
56
|
+
* @returns Listenable: emits backward-seeking point events
|
|
52
57
|
*/
|
|
53
58
|
reverseOnly(): Emitter<PointEvent>;
|
|
59
|
+
filter(check: (event: PointEvent) => boolean): Emitter<PointEvent>;
|
|
60
|
+
/**
|
|
61
|
+
* Creates an emitter that forwards events emitted by seeks of a specific direction
|
|
62
|
+
* @param allow Direction to allow
|
|
63
|
+
* @returns Listenable: emits point events that match the given direction
|
|
64
|
+
*/
|
|
65
|
+
filter(allow: -1 | 1): Emitter<PointEvent>;
|
|
54
66
|
/**
|
|
55
67
|
* Creates a Promise that will be resolved when the Timeline first seeks to/past this point
|
|
56
68
|
*
|
|
@@ -79,4 +91,9 @@ export declare class TimelinePoint extends Emitter<PointEvent> {
|
|
|
79
91
|
* @returns A function to deregister both handlers
|
|
80
92
|
*/
|
|
81
93
|
applyDirectional(apply: () => void, revert: () => void): UnsubscribeFunc;
|
|
94
|
+
/**
|
|
95
|
+
* Creates an emitter that forwards point events whose direction differs from the previous emission
|
|
96
|
+
* @returns Listenable: emits non-repeating point events
|
|
97
|
+
*/
|
|
98
|
+
dedupe(): Emitter<PointEvent>;
|
|
82
99
|
}
|
package/internal/point.js
CHANGED
|
@@ -9,7 +9,9 @@ export class TimelinePoint extends Emitter {
|
|
|
9
9
|
super(onListen);
|
|
10
10
|
this.timeline = timeline;
|
|
11
11
|
this.position = position;
|
|
12
|
-
|
|
12
|
+
}
|
|
13
|
+
redirect(listen) {
|
|
14
|
+
return new TimelinePoint(listen, this.timeline, this.position);
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Creates a range on the Timeline, with a given duration, starting at this point
|
|
@@ -43,27 +45,28 @@ export class TimelinePoint extends Emitter {
|
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
45
47
|
* Creates an emitter that only emits on forward-moving seeks
|
|
46
|
-
* @returns
|
|
48
|
+
* @returns Listenable: emits forward-seeking point events
|
|
47
49
|
*/
|
|
48
50
|
forwardOnly() {
|
|
49
|
-
return
|
|
50
|
-
return this.onListen((ev) => {
|
|
51
|
-
if (ev.direction > 0)
|
|
52
|
-
handler(ev);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
51
|
+
return this.filter(1);
|
|
55
52
|
}
|
|
56
53
|
/**
|
|
57
54
|
* Creates an emitter that only emits on backward-moving seeks
|
|
58
|
-
* @returns
|
|
55
|
+
* @returns Listenable: emits backward-seeking point events
|
|
59
56
|
*/
|
|
60
57
|
reverseOnly() {
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
return this.filter(-1);
|
|
59
|
+
}
|
|
60
|
+
filter(arg) {
|
|
61
|
+
return new Emitter(typeof arg == "number"
|
|
62
|
+
? handler => this.onListen(ev => {
|
|
63
|
+
if (ev.direction === arg)
|
|
64
64
|
handler(ev);
|
|
65
|
-
})
|
|
66
|
-
|
|
65
|
+
})
|
|
66
|
+
: handler => this.onListen((ev) => {
|
|
67
|
+
if (arg(ev))
|
|
68
|
+
handler(ev);
|
|
69
|
+
}));
|
|
67
70
|
}
|
|
68
71
|
/**
|
|
69
72
|
* Creates a Promise that will be resolved when the Timeline first seeks to/past this point
|
|
@@ -104,4 +107,17 @@ export class TimelinePoint extends Emitter {
|
|
|
104
107
|
? apply()
|
|
105
108
|
: revert());
|
|
106
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Creates an emitter that forwards point events whose direction differs from the previous emission
|
|
112
|
+
* @returns Listenable: emits non-repeating point events
|
|
113
|
+
*/
|
|
114
|
+
dedupe() {
|
|
115
|
+
let previous = 0;
|
|
116
|
+
return new Emitter(handler => this.onListen(event => {
|
|
117
|
+
if (event.direction !== previous) {
|
|
118
|
+
handler(event);
|
|
119
|
+
previous = event.direction;
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
107
123
|
}
|
package/internal/range.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare class TimelineRange extends RangeProgression {
|
|
|
16
16
|
constructor(onListen: ListenFunc<number>, timeline: Timeline, startPosition: number,
|
|
17
17
|
/** The duration of this range */
|
|
18
18
|
duration: number);
|
|
19
|
-
protected redirect
|
|
19
|
+
protected redirect(listen: ListenFunc<number>): TimelineRange;
|
|
20
20
|
/**
|
|
21
21
|
* Creates two ranges by seperating one at a given point
|
|
22
22
|
* @param position Point of separation, relative to the range's start - if omitted, the range will be separated halfway
|
|
@@ -48,6 +48,7 @@ export declare class TimelineRange extends RangeProgression {
|
|
|
48
48
|
* Progresses the Timeline across the range at 1000 units per second
|
|
49
49
|
* @param easer Optional easing function
|
|
50
50
|
* @returns Promise, resolved when the end is reached
|
|
51
|
+
* @deprecated Use timeline.play(range, easer?)
|
|
51
52
|
*/
|
|
52
53
|
play(easer?: Easer | keyof typeof easers): Promise<void>;
|
|
53
54
|
/**
|
|
@@ -69,7 +70,7 @@ export declare class TimelineRange extends RangeProgression {
|
|
|
69
70
|
* @param point The point to check
|
|
70
71
|
* @returns true if the provided point is within the range
|
|
71
72
|
*/
|
|
72
|
-
contains(point: TimelinePoint): boolean;
|
|
73
|
+
contains(point: TimelinePoint | number): boolean;
|
|
73
74
|
/**
|
|
74
75
|
* Checks if a range is fully within this range
|
|
75
76
|
* @param range The range to check
|
package/internal/range.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { RangeProgression } from "./emitters";
|
|
2
2
|
import { TimelinePoint } from "./point";
|
|
3
|
-
import { clamp } from "./utils";
|
|
4
3
|
export class TimelineRange extends RangeProgression {
|
|
5
4
|
/** @internal Manual construction of RangeProgression is outside of the API contract and subject to undocumented change */
|
|
6
5
|
constructor(onListen, timeline, startPosition,
|
|
@@ -14,11 +13,13 @@ export class TimelineRange extends RangeProgression {
|
|
|
14
13
|
this.timeline = timeline;
|
|
15
14
|
this.startPosition = startPosition;
|
|
16
15
|
this.duration = duration;
|
|
17
|
-
this.redirect = (listen) => new TimelineRange(listen, this.timeline, this.startPosition, this.duration);
|
|
18
16
|
this.endPosition = startPosition + duration;
|
|
19
17
|
this.end = timeline.point(this.endPosition);
|
|
20
18
|
this.start = timeline.point(startPosition);
|
|
21
19
|
}
|
|
20
|
+
redirect(listen) {
|
|
21
|
+
return new TimelineRange(listen, this.timeline, this.startPosition, this.duration);
|
|
22
|
+
}
|
|
22
23
|
/**
|
|
23
24
|
* Creates two ranges by seperating one at a given point
|
|
24
25
|
* @param position Point of separation, relative to the range's start - if omitted, the range will be separated halfway
|
|
@@ -28,8 +29,8 @@ export class TimelineRange extends RangeProgression {
|
|
|
28
29
|
*/
|
|
29
30
|
bisect(position = this.duration / 2) {
|
|
30
31
|
return [
|
|
31
|
-
this.timeline.range(
|
|
32
|
-
this.timeline.range(position + this.startPosition, this.duration -
|
|
32
|
+
this.timeline.range(this.startPosition, position),
|
|
33
|
+
this.timeline.range(position + this.startPosition, this.duration - position),
|
|
33
34
|
];
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
@@ -65,11 +66,12 @@ export class TimelineRange extends RangeProgression {
|
|
|
65
66
|
* Progresses the Timeline across the range at 1000 units per second
|
|
66
67
|
* @param easer Optional easing function
|
|
67
68
|
* @returns Promise, resolved when the end is reached
|
|
69
|
+
* @deprecated Use timeline.play(range, easer?)
|
|
68
70
|
*/
|
|
69
71
|
play(easer) {
|
|
70
72
|
this.timeline.pause();
|
|
71
73
|
this.timeline.currentTime = this.startPosition;
|
|
72
|
-
return this.timeline.seek(this.
|
|
74
|
+
return this.timeline.seek(this.end, this.duration, easer);
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
77
|
* Creates a new range representing a direct expansion of this one
|
|
@@ -78,16 +80,7 @@ export class TimelineRange extends RangeProgression {
|
|
|
78
80
|
* @returns Listenable: this range will emit a progression value (0..1) when a `seek()` passes or intersects it
|
|
79
81
|
*/
|
|
80
82
|
grow(delta, anchor = 0) {
|
|
81
|
-
|
|
82
|
-
const leftDelta = -delta * (1 - clampedAnchor);
|
|
83
|
-
const rightDelta = delta * clampedAnchor;
|
|
84
|
-
const newStart = this.startPosition + leftDelta;
|
|
85
|
-
const newEnd = this.startPosition + this.duration + rightDelta;
|
|
86
|
-
if (newEnd < newStart) {
|
|
87
|
-
const mid = (newStart + newEnd) / 2;
|
|
88
|
-
return this.timeline.range(mid, 0);
|
|
89
|
-
}
|
|
90
|
-
return this.timeline.range(newStart, newEnd - newStart);
|
|
83
|
+
return this.timeline.range(this.startPosition - (delta * anchor), this.duration + delta);
|
|
91
84
|
}
|
|
92
85
|
/**
|
|
93
86
|
* Creates a new range representing a multiplicative expansion of this one
|
|
@@ -99,18 +92,12 @@ export class TimelineRange extends RangeProgression {
|
|
|
99
92
|
if (factor <= 0) {
|
|
100
93
|
throw new RangeError('Scale factor must be > 0');
|
|
101
94
|
}
|
|
102
|
-
|
|
103
|
-
const oldLen = this.endPosition - this.startPosition;
|
|
104
|
-
const pivot = this.startPosition + oldLen * clampedAnchor;
|
|
105
|
-
const newStart = pivot - (pivot - this.startPosition) * factor;
|
|
106
|
-
const newEnd = pivot + (this.endPosition - pivot) * factor;
|
|
107
|
-
if (newEnd < newStart) {
|
|
108
|
-
const mid = (newStart + newEnd) / 2;
|
|
109
|
-
return this.timeline.range(mid, 0);
|
|
110
|
-
}
|
|
111
|
-
return this.timeline.range(newStart, newEnd - newStart);
|
|
95
|
+
return this.grow((factor - 1) * this.duration, anchor);
|
|
112
96
|
}
|
|
113
97
|
contains(target) {
|
|
98
|
+
if (typeof target == "number") {
|
|
99
|
+
return target >= this.startPosition && target < this.endPosition;
|
|
100
|
+
}
|
|
114
101
|
const [targetStart, targetEnd] = target instanceof TimelinePoint
|
|
115
102
|
? [target.position, target.position]
|
|
116
103
|
: [target.startPosition, target.startPosition + target.duration];
|
package/internal/timeline.d.ts
CHANGED
|
@@ -10,12 +10,16 @@ declare const EndAction: {
|
|
|
10
10
|
readonly wrap: 2;
|
|
11
11
|
readonly restart: 3;
|
|
12
12
|
};
|
|
13
|
+
type Period = {
|
|
14
|
+
asMilliseconds: number;
|
|
15
|
+
};
|
|
13
16
|
/**
|
|
14
17
|
* Creates an autoplaying Timeline and returns a range from it
|
|
15
|
-
* @param
|
|
18
|
+
* @param durationMs Animation duration, in milliseconds
|
|
16
19
|
* @returns Object representing a range on a single-use, autoplaying Timeline
|
|
17
20
|
*/
|
|
18
|
-
export declare function animate(
|
|
21
|
+
export declare function animate(durationMs: number): TimelineRange;
|
|
22
|
+
export declare function animate(period: Period): TimelineRange;
|
|
19
23
|
export declare class Timeline {
|
|
20
24
|
/**
|
|
21
25
|
* Multiplies the speed at which `play()` progresses through the Timeline
|
|
@@ -89,12 +93,12 @@ export declare class Timeline {
|
|
|
89
93
|
* Defines a range on this Timeline
|
|
90
94
|
*
|
|
91
95
|
* @param start The position on this Timeline at which the range starts
|
|
92
|
-
* @param duration Length of the resulting range
|
|
96
|
+
* @param duration Length of the resulting range
|
|
93
97
|
* @returns A range on the Timeline
|
|
94
98
|
*
|
|
95
99
|
* Listenable: this range will emit a progression value (0..1) when a `seek()` passes or intersects it
|
|
96
100
|
*/
|
|
97
|
-
range(start: number | TimelinePoint, duration
|
|
101
|
+
range(start: number | TimelinePoint, duration: number): TimelineRange;
|
|
98
102
|
/**
|
|
99
103
|
* Creates an observable range from position 0 to the Timeline's **current** final position
|
|
100
104
|
*/
|
|
@@ -114,7 +118,8 @@ export declare class Timeline {
|
|
|
114
118
|
* @param easer Optional easing function for the smooth-seek process
|
|
115
119
|
* @returns A promise, resolved when the smooth-seek process finishes
|
|
116
120
|
*/
|
|
117
|
-
seek(toPosition: number | TimelinePoint,
|
|
121
|
+
seek(toPosition: number | TimelinePoint, durationMs: number, easer?: Easer | keyof typeof easers): Promise<void>;
|
|
122
|
+
seek(toPosition: number | TimelinePoint, duration: Period, easer?: Easer | keyof typeof easers): Promise<void>;
|
|
118
123
|
private seekDirect;
|
|
119
124
|
private seekPoints;
|
|
120
125
|
private seekRanges;
|
|
@@ -124,6 +129,16 @@ export declare class Timeline {
|
|
|
124
129
|
*/
|
|
125
130
|
play(): void;
|
|
126
131
|
play(fps: number): void;
|
|
132
|
+
/**
|
|
133
|
+
* Performs a smooth-seek through a range at (1000 × this.timeScale) units per second
|
|
134
|
+
*/
|
|
135
|
+
play(range: TimelineRange, easer?: Easer): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Stops normal progression instigated by play()
|
|
138
|
+
*
|
|
139
|
+
* Does not affect ongoing smooth-seek operations or play(range)
|
|
140
|
+
*
|
|
141
|
+
*/
|
|
127
142
|
pause(): void;
|
|
128
143
|
/**
|
|
129
144
|
* Progresses the Timeline by 1 unit
|
|
@@ -148,7 +163,7 @@ export declare class Timeline {
|
|
|
148
163
|
get position(): number;
|
|
149
164
|
}
|
|
150
165
|
export interface ChainingInterface {
|
|
151
|
-
thenTween<T extends Tweenable>(duration: number, apply: (v: Widen<T>) => void, from: T, to: T, easer
|
|
166
|
+
thenTween<T extends Tweenable>(duration: number, apply: (v: Widen<T>) => void, from: T, to: T, easer?: Easer): ChainingInterface;
|
|
152
167
|
then(action: () => void): ChainingInterface;
|
|
153
168
|
thenWait(duration: number): ChainingInterface;
|
|
154
169
|
fork(fn: (chain: ChainingInterface) => void): ChainingInterface;
|
package/internal/timeline.js
CHANGED
|
@@ -9,13 +9,11 @@ const EndAction = {
|
|
|
9
9
|
wrap: 2,
|
|
10
10
|
restart: 3,
|
|
11
11
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export function animate(duration) {
|
|
18
|
-
return new Timeline(true).range(0, duration);
|
|
12
|
+
export function animate(durationMs) {
|
|
13
|
+
return new Timeline(true)
|
|
14
|
+
.range(0, typeof durationMs == "number"
|
|
15
|
+
? durationMs
|
|
16
|
+
: durationMs.asMilliseconds);
|
|
19
17
|
}
|
|
20
18
|
export class Timeline {
|
|
21
19
|
get currentTime() { return this._currentTime; }
|
|
@@ -132,7 +130,7 @@ export class Timeline {
|
|
|
132
130
|
//if (endPosition > this._endPosition) this._endPosition = endPosition;
|
|
133
131
|
// ^ leave this to range's point() calls
|
|
134
132
|
const handlers = [];
|
|
135
|
-
const
|
|
133
|
+
const rangeData = {
|
|
136
134
|
position: startPosition,
|
|
137
135
|
duration,
|
|
138
136
|
handlers,
|
|
@@ -141,22 +139,28 @@ export class Timeline {
|
|
|
141
139
|
if (this.seeking)
|
|
142
140
|
throw new Error("Can't add a listener while seeking");
|
|
143
141
|
if (handlers.length == 0) {
|
|
144
|
-
this.ranges.push(
|
|
142
|
+
this.ranges.push(rangeData);
|
|
145
143
|
this.currentSortDirection = 0;
|
|
146
144
|
}
|
|
147
145
|
handlers.push(handler);
|
|
146
|
+
// if currentTime is in this range, apply immediately
|
|
147
|
+
if (range.contains(this._currentTime)) {
|
|
148
|
+
let progress = clamp((this._currentTime - startPosition) / duration, 0, 1);
|
|
149
|
+
handler(progress);
|
|
150
|
+
}
|
|
148
151
|
return () => {
|
|
149
152
|
const idx = handlers.indexOf(handler);
|
|
150
153
|
if (idx === -1)
|
|
151
154
|
throw new Error("Internal error: attempting to remove a non-present handler");
|
|
152
155
|
handlers.splice(idx, 1);
|
|
153
156
|
if (handlers.length == 0) {
|
|
154
|
-
const idx = this.ranges.indexOf(
|
|
157
|
+
const idx = this.ranges.indexOf(rangeData);
|
|
155
158
|
this.ranges.splice(idx, 1);
|
|
156
159
|
}
|
|
157
160
|
};
|
|
158
161
|
};
|
|
159
|
-
|
|
162
|
+
const range = new TimelineRange(addHandler, this, startPosition, duration);
|
|
163
|
+
return range;
|
|
160
164
|
}
|
|
161
165
|
getWrappedPosition(n) {
|
|
162
166
|
if (this.endAction.type !== EndAction.wrap)
|
|
@@ -175,26 +179,36 @@ export class Timeline {
|
|
|
175
179
|
return loopStart + remainder;
|
|
176
180
|
}
|
|
177
181
|
seek(to, duration = 0, easer) {
|
|
182
|
+
const durationMs = typeof duration == "number"
|
|
183
|
+
? duration
|
|
184
|
+
: duration.asMilliseconds;
|
|
178
185
|
const toPosition = typeof to == "number"
|
|
179
186
|
? to
|
|
180
187
|
: to.position;
|
|
181
188
|
if (this.seeking) {
|
|
182
|
-
throw new Error("Can't seek while
|
|
189
|
+
throw new Error("Can't seek while a seek event is processed");
|
|
183
190
|
}
|
|
184
191
|
if (this.smoothSeeker !== null) {
|
|
185
192
|
this.smoothSeeker.pause();
|
|
186
|
-
// ensure any awaits are resolved for the
|
|
187
|
-
this.
|
|
193
|
+
// ensure any awaits are resolved for the interrupted seek
|
|
194
|
+
const interruptPosition = this._currentTime;
|
|
195
|
+
this.smoothSeeker.seek(this.smoothSeeker.end);
|
|
188
196
|
this.smoothSeeker = null;
|
|
197
|
+
// and jump back to where we were interrupted
|
|
198
|
+
this.seek(interruptPosition);
|
|
189
199
|
}
|
|
190
|
-
if (
|
|
200
|
+
if (durationMs === 0) {
|
|
191
201
|
this.seekDirect(toPosition);
|
|
192
202
|
return Promise.resolve();
|
|
193
203
|
}
|
|
194
204
|
const seeker = new Timeline(true);
|
|
195
205
|
this.smoothSeeker = seeker;
|
|
196
|
-
seeker
|
|
197
|
-
|
|
206
|
+
seeker
|
|
207
|
+
.range(0, durationMs)
|
|
208
|
+
.ease(easer)
|
|
209
|
+
.tween(this.currentTime, toPosition)
|
|
210
|
+
.apply(v => this.seekDirect(v));
|
|
211
|
+
return seeker.end.promise();
|
|
198
212
|
}
|
|
199
213
|
seekDirect(toPosition) {
|
|
200
214
|
const fromPosition = this._currentTime;
|
|
@@ -242,6 +256,8 @@ export class Timeline {
|
|
|
242
256
|
});
|
|
243
257
|
}
|
|
244
258
|
seekRanges(to) {
|
|
259
|
+
if (this._currentTime === to)
|
|
260
|
+
return;
|
|
245
261
|
const fromTime = Math.min(this._currentTime, to);
|
|
246
262
|
const toTime = Math.max(this._currentTime, to);
|
|
247
263
|
this.ranges.slice().forEach((range) => {
|
|
@@ -252,7 +268,7 @@ export class Timeline {
|
|
|
252
268
|
range.handlers.slice().forEach(h => h(progress));
|
|
253
269
|
}
|
|
254
270
|
});
|
|
255
|
-
this.progressionHandlers.slice().forEach(h => h(
|
|
271
|
+
this.progressionHandlers.slice().forEach(h => h(toTime / this._endPosition));
|
|
256
272
|
}
|
|
257
273
|
sortEntries(direction) {
|
|
258
274
|
this.currentSortDirection = direction;
|
|
@@ -263,9 +279,18 @@ export class Timeline {
|
|
|
263
279
|
? sortTweens
|
|
264
280
|
: sortReverse);
|
|
265
281
|
}
|
|
266
|
-
play(
|
|
282
|
+
play(arg = default_fps, easer) {
|
|
267
283
|
if (this.interval !== null)
|
|
268
284
|
this.pause();
|
|
285
|
+
if (this.smoothSeeker) {
|
|
286
|
+
this.smoothSeeker.pause();
|
|
287
|
+
this.smoothSeeker.seek(this.smoothSeeker.end);
|
|
288
|
+
this.smoothSeeker = null;
|
|
289
|
+
}
|
|
290
|
+
if (arg instanceof TimelineRange) {
|
|
291
|
+
this.seek(arg.start);
|
|
292
|
+
return this.seek(arg.end, arg.duration / this.timeScale, easer);
|
|
293
|
+
}
|
|
269
294
|
let previousTime = Date.now();
|
|
270
295
|
this.interval = setInterval(() => {
|
|
271
296
|
const newTime = Date.now();
|
|
@@ -276,7 +301,7 @@ export class Timeline {
|
|
|
276
301
|
this.currentTime += delta;
|
|
277
302
|
return;
|
|
278
303
|
}
|
|
279
|
-
// overshot; perform endAction
|
|
304
|
+
// overshot; perform restart/pause endAction
|
|
280
305
|
if (this.endAction.type == EndAction.restart) {
|
|
281
306
|
const loopRange = this.endAction.at.to(this._endPosition);
|
|
282
307
|
const loopLen = loopRange.duration;
|
|
@@ -303,8 +328,14 @@ export class Timeline {
|
|
|
303
328
|
return;
|
|
304
329
|
}
|
|
305
330
|
this.currentTime += delta;
|
|
306
|
-
}, 1000 /
|
|
331
|
+
}, 1000 / arg);
|
|
307
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Stops normal progression instigated by play()
|
|
335
|
+
*
|
|
336
|
+
* Does not affect ongoing smooth-seek operations or play(range)
|
|
337
|
+
*
|
|
338
|
+
*/
|
|
308
339
|
pause() {
|
|
309
340
|
if (this.interval === null)
|
|
310
341
|
return;
|
package/internal/tween.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export type Tweenable = number | number[] | string | string[] | Blendable | Blendable[];
|
|
2
|
+
export type Tweenable = number | number[] | string | string[] | Blendable | Date | Blendable[];
|
|
3
3
|
/** @internal */
|
|
4
4
|
export interface Blendable {
|
|
5
5
|
blend(target: this, progress: number): this;
|
package/internal/tween.js
CHANGED
|
@@ -18,7 +18,11 @@ export function createTween(from, to) {
|
|
|
18
18
|
}
|
|
19
19
|
switch (typeof from) {
|
|
20
20
|
case "number": return progress => blendNumbers(from, to, progress);
|
|
21
|
-
case "object":
|
|
21
|
+
case "object": {
|
|
22
|
+
if (from instanceof Date)
|
|
23
|
+
return progress => new Date(blendNumbers(from.getTime(), to.getTime(), progress));
|
|
24
|
+
return progress => from.blend(to, progress);
|
|
25
|
+
}
|
|
22
26
|
case "string": return createStringTween(from, to);
|
|
23
27
|
default: throw new Error("Invalid tweening type");
|
|
24
28
|
}
|