@xtia/timeline 1.1.7 → 1.1.9
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 +8 -8
- package/internal/point.d.ts +4 -4
- package/internal/point.js +4 -4
- package/internal/timeline.d.ts +14 -2
- package/internal/timeline.js +88 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -515,6 +515,14 @@ Listeners will be invoked with a [`PointEvent`](#pointevent-interface) when a se
|
|
|
515
515
|
|
|
516
516
|
This point's position on the Timeline.
|
|
517
517
|
|
|
518
|
+
##### `forwardOnly: Emitter<PointEvent>`
|
|
519
|
+
|
|
520
|
+
Provides an emitter that forwards emissions triggered by forward-moving seeks.
|
|
521
|
+
|
|
522
|
+
##### `reverseOnly: Emitter<PointEvent>`
|
|
523
|
+
|
|
524
|
+
Provides an emitter that forwards emissions triggered by backward-moving seeks.
|
|
525
|
+
|
|
518
526
|
#### Methods
|
|
519
527
|
|
|
520
528
|
##### `range(duration): TimelineRange`
|
|
@@ -543,14 +551,6 @@ Creates a `Promise` that will be resolved when the Timeline first seeks to/past
|
|
|
543
551
|
|
|
544
552
|
The resolved value indicates the direction of the seek that triggered resolution.
|
|
545
553
|
|
|
546
|
-
##### `forwardOnly(): Emitter<PointEvent>`
|
|
547
|
-
|
|
548
|
-
Creates an emitter that forwards emissions triggered by forward-moving seeks.
|
|
549
|
-
|
|
550
|
-
##### `reverseOnly(): Emitter<PointEvent>`
|
|
551
|
-
|
|
552
|
-
Creates an emitter that forwards emissions triggered by backward-moving seeks.
|
|
553
|
-
|
|
554
554
|
##### `applyDirectional(apply, revert): UnsubscribeFunc`
|
|
555
555
|
|
|
556
556
|
Registers an emission handler that calls one function for forward seeks to or past the point, and another for backward seeks from or past the point.
|
package/internal/point.d.ts
CHANGED
|
@@ -46,16 +46,16 @@ export declare class TimelinePoint extends Emitter<PointEvent> {
|
|
|
46
46
|
*/
|
|
47
47
|
seek(duration: number, easer?: Easer): Promise<void>;
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
49
|
+
* An point emitter that only emits on forward-moving seeks
|
|
50
50
|
* @returns Listenable: emits forward-seeking point events
|
|
51
51
|
*/
|
|
52
|
-
forwardOnly(): Emitter<PointEvent>;
|
|
52
|
+
get forwardOnly(): Emitter<PointEvent>;
|
|
53
53
|
private _forwardOnly?;
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* An point emitter that only emits on backward-moving seeks
|
|
56
56
|
* @returns Listenable: emits backward-seeking point events
|
|
57
57
|
*/
|
|
58
|
-
reverseOnly(): Emitter<PointEvent>;
|
|
58
|
+
get reverseOnly(): Emitter<PointEvent>;
|
|
59
59
|
private _reverseOnly?;
|
|
60
60
|
filter(check: (event: PointEvent) => boolean): Emitter<PointEvent>;
|
|
61
61
|
/**
|
package/internal/point.js
CHANGED
|
@@ -41,19 +41,19 @@ export class TimelinePoint extends Emitter {
|
|
|
41
41
|
return this.timeline.seek(this.position, duration, easer);
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* An point emitter that only emits on forward-moving seeks
|
|
45
45
|
* @returns Listenable: emits forward-seeking point events
|
|
46
46
|
*/
|
|
47
|
-
forwardOnly() {
|
|
47
|
+
get forwardOnly() {
|
|
48
48
|
if (!this._forwardOnly)
|
|
49
49
|
this._forwardOnly = this.filter(1);
|
|
50
50
|
return this._forwardOnly;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* An point emitter that only emits on backward-moving seeks
|
|
54
54
|
* @returns Listenable: emits backward-seeking point events
|
|
55
55
|
*/
|
|
56
|
-
reverseOnly() {
|
|
56
|
+
get reverseOnly() {
|
|
57
57
|
if (!this._reverseOnly)
|
|
58
58
|
this._reverseOnly = this.filter(-1);
|
|
59
59
|
return this._reverseOnly;
|
package/internal/timeline.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare class Timeline {
|
|
|
42
42
|
get end(): TimelinePoint;
|
|
43
43
|
private _currentTime;
|
|
44
44
|
private _endPosition;
|
|
45
|
-
private
|
|
45
|
+
private _pause;
|
|
46
46
|
private points;
|
|
47
47
|
private endAction;
|
|
48
48
|
private ranges;
|
|
@@ -118,7 +118,8 @@ export declare class Timeline {
|
|
|
118
118
|
*/
|
|
119
119
|
range(): TimelineRange;
|
|
120
120
|
/**
|
|
121
|
-
* Seeks the Timeline to a specified position, triggering in order any point and range
|
|
121
|
+
* Seeks the Timeline to a specified position, triggering in order any point and range
|
|
122
|
+
* subscriptions between its current and new positions
|
|
122
123
|
* @param toPosition
|
|
123
124
|
*/
|
|
124
125
|
seek(toPosition: number | TimelinePoint): void;
|
|
@@ -133,6 +134,14 @@ export declare class Timeline {
|
|
|
133
134
|
*/
|
|
134
135
|
seek(toPosition: number | TimelinePoint, durationMs: number, easer?: Easer | keyof typeof easers): Promise<void>;
|
|
135
136
|
seek(toPosition: number | TimelinePoint, duration: Period, easer?: Easer | keyof typeof easers): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Smooth-seeks through a range over a given duration
|
|
139
|
+
* @param range The range to seek through
|
|
140
|
+
* @param durationMs Smooth-seek duration
|
|
141
|
+
* @param easer Optional easing function
|
|
142
|
+
*/
|
|
143
|
+
seek(range: TimelineRange, durationMs: number, easer?: Easer | keyof typeof easers): Promise<void>;
|
|
144
|
+
seek(range: TimelineRange, duration: Period, easer?: Easer | keyof typeof easers): Promise<void>;
|
|
136
145
|
private seekDirect;
|
|
137
146
|
private seekWrapped;
|
|
138
147
|
private seekPoints;
|
|
@@ -147,6 +156,9 @@ export declare class Timeline {
|
|
|
147
156
|
* Performs a smooth-seek through a range at (1000 × this.timeScale) units per second
|
|
148
157
|
*/
|
|
149
158
|
play(range: TimelineRange, easer?: Easer): Promise<void>;
|
|
159
|
+
private playWithInterval;
|
|
160
|
+
private playWithRAF;
|
|
161
|
+
private next;
|
|
150
162
|
/**
|
|
151
163
|
* Stops normal progression instigated by play()
|
|
152
164
|
*
|
package/internal/timeline.js
CHANGED
|
@@ -3,6 +3,8 @@ import { TimelinePoint } from "./point";
|
|
|
3
3
|
import { TimelineRange } from "./range";
|
|
4
4
|
import { clamp } from "./utils";
|
|
5
5
|
const default_fps = 60;
|
|
6
|
+
const requestAnimFrame = globalThis?.requestAnimationFrame;
|
|
7
|
+
const cancelAnimFrame = globalThis?.cancelAnimationFrame;
|
|
6
8
|
const EndAction = {
|
|
7
9
|
pause: 0,
|
|
8
10
|
continue: 1,
|
|
@@ -27,7 +29,7 @@ export class Timeline {
|
|
|
27
29
|
* Returns true if this Timeline is currently progressing via `play()`, otherwise false
|
|
28
30
|
*/
|
|
29
31
|
get isPlaying() {
|
|
30
|
-
return this.
|
|
32
|
+
return !!this._pause;
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Returns a fixed point at the current end of the Timeline
|
|
@@ -71,7 +73,7 @@ export class Timeline {
|
|
|
71
73
|
this.timeScale = 1;
|
|
72
74
|
this._currentTime = 0;
|
|
73
75
|
this._endPosition = 0;
|
|
74
|
-
this.
|
|
76
|
+
this._pause = null;
|
|
75
77
|
this.points = [];
|
|
76
78
|
this.ranges = [];
|
|
77
79
|
this.currentSortDirection = 0;
|
|
@@ -174,6 +176,10 @@ export class Timeline {
|
|
|
174
176
|
return range;
|
|
175
177
|
}
|
|
176
178
|
seek(to, duration, easer) {
|
|
179
|
+
if (to instanceof TimelineRange) {
|
|
180
|
+
this.seek(to.start);
|
|
181
|
+
return this.seek(to, duration, easer);
|
|
182
|
+
}
|
|
177
183
|
const durationMs = typeof duration == "object"
|
|
178
184
|
? duration.asMilliseconds
|
|
179
185
|
: duration;
|
|
@@ -193,7 +199,6 @@ export class Timeline {
|
|
|
193
199
|
this.seekDirect(interruptPosition);
|
|
194
200
|
}
|
|
195
201
|
if (!durationMs) {
|
|
196
|
-
const fromTime = this._currentTime;
|
|
197
202
|
this.seekDirect(toPosition);
|
|
198
203
|
this._frameEvents?.emit();
|
|
199
204
|
// only add Promise overhead if duration is explicitly 0
|
|
@@ -311,9 +316,8 @@ export class Timeline {
|
|
|
311
316
|
? sortTweens
|
|
312
317
|
: sortReverse);
|
|
313
318
|
}
|
|
314
|
-
play(arg
|
|
315
|
-
|
|
316
|
-
this.pause();
|
|
319
|
+
play(arg, easer) {
|
|
320
|
+
this._pause?.();
|
|
317
321
|
if (this.smoothSeeker) {
|
|
318
322
|
this.smoothSeeker.pause();
|
|
319
323
|
this.smoothSeeker.seek(this.smoothSeeker.end);
|
|
@@ -323,44 +327,73 @@ export class Timeline {
|
|
|
323
327
|
this.seek(arg.start);
|
|
324
328
|
return this.seek(arg.end, arg.duration / this.timeScale, easer);
|
|
325
329
|
}
|
|
330
|
+
if (arg !== undefined && requestAnimFrame) {
|
|
331
|
+
this.playWithRAF();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
this.playWithInterval(arg ?? default_fps);
|
|
335
|
+
}
|
|
336
|
+
playWithInterval(fps) {
|
|
326
337
|
let previousTime = Date.now();
|
|
327
|
-
|
|
338
|
+
const interval = setInterval(() => {
|
|
328
339
|
const newTime = Date.now();
|
|
329
340
|
const elapsed = newTime - previousTime;
|
|
330
341
|
previousTime = newTime;
|
|
331
342
|
let delta = elapsed * this.timeScale;
|
|
332
|
-
|
|
333
|
-
|
|
343
|
+
this.next(delta);
|
|
344
|
+
}, 1000 / fps);
|
|
345
|
+
this._pause = () => clearInterval(interval);
|
|
346
|
+
}
|
|
347
|
+
playWithRAF() {
|
|
348
|
+
let previousTime = null;
|
|
349
|
+
let rafId;
|
|
350
|
+
const frame = (currentTime) => {
|
|
351
|
+
if (previousTime === null) {
|
|
352
|
+
previousTime = currentTime;
|
|
353
|
+
}
|
|
354
|
+
const elapsed = currentTime - previousTime;
|
|
355
|
+
previousTime = currentTime;
|
|
356
|
+
let delta = elapsed * this.timeScale;
|
|
357
|
+
this.next(delta);
|
|
358
|
+
if (this._pause)
|
|
359
|
+
rafId = requestAnimFrame(frame);
|
|
360
|
+
};
|
|
361
|
+
rafId = requestAnimFrame(frame);
|
|
362
|
+
this._pause = () => cancelAnimFrame(rafId);
|
|
363
|
+
}
|
|
364
|
+
next(delta) {
|
|
365
|
+
if (this._currentTime + delta <= this._endPosition) {
|
|
366
|
+
this.currentTime += delta;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// overshot; perform restart/pause endAction
|
|
370
|
+
if (this.endAction.type == EndAction.restart) {
|
|
371
|
+
const loopRange = this.endAction.at.to(this._endPosition);
|
|
372
|
+
const loopLen = loopRange.duration;
|
|
373
|
+
if (loopLen <= 0) {
|
|
374
|
+
const target = Math.min(this._currentTime + delta, this._endPosition);
|
|
375
|
+
this.seek(target);
|
|
334
376
|
return;
|
|
335
377
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (loopLen <= 0) {
|
|
341
|
-
const target = Math.min(this._currentTime + delta, this._endPosition);
|
|
342
|
-
this.seek(target);
|
|
378
|
+
while (delta > 0) {
|
|
379
|
+
const distanceToEnd = this._endPosition - this._currentTime;
|
|
380
|
+
if (delta < distanceToEnd) {
|
|
381
|
+
this.seek(this._currentTime + delta);
|
|
343
382
|
return;
|
|
344
383
|
}
|
|
345
|
-
while (delta > 0) {
|
|
346
|
-
const distanceToEnd = this._endPosition - this._currentTime;
|
|
347
|
-
if (delta < distanceToEnd) {
|
|
348
|
-
this.seek(this._currentTime + delta);
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
this.seek(this._endPosition);
|
|
352
|
-
delta -= distanceToEnd;
|
|
353
|
-
this.seek(this.endAction.at);
|
|
354
|
-
}
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
if (this.endAction.type == EndAction.pause) {
|
|
358
384
|
this.seek(this._endPosition);
|
|
359
|
-
|
|
360
|
-
|
|
385
|
+
delta -= distanceToEnd;
|
|
386
|
+
this.seek(this.endAction.at);
|
|
361
387
|
}
|
|
362
|
-
|
|
363
|
-
}
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (this.endAction.type == EndAction.pause) {
|
|
391
|
+
this.seek(this._endPosition);
|
|
392
|
+
this.pause();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
// endaction must be "continue" or "wrap"
|
|
396
|
+
this.currentTime += delta;
|
|
364
397
|
}
|
|
365
398
|
/**
|
|
366
399
|
* Stops normal progression instigated by play()
|
|
@@ -369,10 +402,10 @@ export class Timeline {
|
|
|
369
402
|
*
|
|
370
403
|
*/
|
|
371
404
|
pause() {
|
|
372
|
-
if (this.
|
|
405
|
+
if (this._pause === null)
|
|
373
406
|
return;
|
|
374
|
-
|
|
375
|
-
this.
|
|
407
|
+
this._pause();
|
|
408
|
+
this._pause = null;
|
|
376
409
|
}
|
|
377
410
|
step(delta = 1) {
|
|
378
411
|
this.currentTime += delta * this.timeScale;
|
|
@@ -398,12 +431,25 @@ export class Timeline {
|
|
|
398
431
|
*/
|
|
399
432
|
at(position, action, reverse) {
|
|
400
433
|
const point = typeof position == "number" ? this.point(position) : position;
|
|
401
|
-
if (
|
|
402
|
-
reverse
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
434
|
+
if (!action) {
|
|
435
|
+
if (reverse) {
|
|
436
|
+
if (reverse === true)
|
|
437
|
+
throw new Error("Invalid call");
|
|
438
|
+
point.reverseOnly.apply(reverse);
|
|
439
|
+
}
|
|
440
|
+
return this.createChainingInterface(point.position);
|
|
441
|
+
}
|
|
442
|
+
if (reverse) {
|
|
443
|
+
if (reverse === true) {
|
|
444
|
+
point.apply(action);
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
point.applyDirectional(action, reverse);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
point.forwardOnly.apply(action);
|
|
452
|
+
}
|
|
407
453
|
return this.createChainingInterface(point.position);
|
|
408
454
|
}
|
|
409
455
|
createChainingInterface(position) {
|