@xtia/timeline 1.2.1 → 1.2.3
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/index.d.ts +3 -2
- package/index.js +2 -2
- package/internal/animate.d.ts +15 -0
- package/internal/animate.js +32 -0
- package/internal/driver.d.ts +1 -0
- package/internal/driver.js +53 -0
- package/internal/emitters.d.ts +8 -2
- package/internal/emitters.js +7 -1
- package/internal/timeline.d.ts +6 -19
- package/internal/timeline.js +3 -79
- package/internal/utils.d.ts +3 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { Timeline,
|
|
1
|
+
export { Timeline, ChainingInterface } from "./internal/timeline.js";
|
|
2
|
+
export { animate } from "./internal/animate.js";
|
|
2
3
|
export { TimelinePoint, PointEvent } from "./internal/point.js";
|
|
3
4
|
export { TimelineRange } from "./internal/range.js";
|
|
4
|
-
export { Emitter, RangeProgression, UnsubscribeFunc } from "./internal/emitters.js";
|
|
5
|
+
export { type Emitter, type RangeProgression, UnsubscribeFunc } from "./internal/emitters.js";
|
|
5
6
|
export { easers } from "./internal/easing.js";
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { Timeline
|
|
1
|
+
export { Timeline } from "./internal/timeline.js";
|
|
2
|
+
export { animate } from "./internal/animate.js";
|
|
2
3
|
export { TimelinePoint } from "./internal/point.js";
|
|
3
4
|
export { TimelineRange } from "./internal/range.js";
|
|
4
|
-
export { Emitter, RangeProgression } from "./internal/emitters.js";
|
|
5
5
|
export { easers } from "./internal/easing.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RangeProgression } from "./emitters.js";
|
|
2
|
+
import { Period } from "./utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates an autoplaying one-shot progression emitter
|
|
5
|
+
* @param durationMs Animation duration, in milliseconds
|
|
6
|
+
* @returns Object representing a range on a single-use, autoplaying Timeline
|
|
7
|
+
*/
|
|
8
|
+
export declare function animate(durationMs: number): RangeProgression;
|
|
9
|
+
export declare function animate(period: Period): RangeProgression;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a looping progression emitter that will play while it has active listeners
|
|
12
|
+
* @param duration Animation duration, in milliseconds, or a Period
|
|
13
|
+
* @returns Object representing a range on a looping Timeline
|
|
14
|
+
*/
|
|
15
|
+
export declare function animate(duration: number | Period, looping: true): RangeProgression;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { masterDriver } from "./driver.js";
|
|
2
|
+
import { createListenable, RangeProgression } from "./emitters.js";
|
|
3
|
+
export function animate(duration, looping = false) {
|
|
4
|
+
const durationMs = typeof duration == "number"
|
|
5
|
+
? duration
|
|
6
|
+
: duration.asMilliseconds;
|
|
7
|
+
if (durationMs === Infinity || durationMs < 0)
|
|
8
|
+
throw new RangeError("animate() duration must be positive and finite");
|
|
9
|
+
let t = 0;
|
|
10
|
+
if (looping) {
|
|
11
|
+
const { emit, listen } = createListenable(() => masterDriver(delta => {
|
|
12
|
+
t += delta;
|
|
13
|
+
emit((t / durationMs) % 1);
|
|
14
|
+
}));
|
|
15
|
+
return new RangeProgression(h => {
|
|
16
|
+
h(t);
|
|
17
|
+
return listen(h);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const { emit, listen } = createListenable();
|
|
21
|
+
const masterUnsub = masterDriver(delta => {
|
|
22
|
+
t = Math.min(durationMs, t + delta);
|
|
23
|
+
emit(t / durationMs);
|
|
24
|
+
if (t === duration) {
|
|
25
|
+
masterUnsub();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return new RangeProgression(h => {
|
|
29
|
+
h(t);
|
|
30
|
+
return listen(h);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const masterDriver: (stepFn: (n: number) => void) => () => void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const default_interval_fps = 60;
|
|
2
|
+
const createRafDriver = (tick) => {
|
|
3
|
+
let rafId = null;
|
|
4
|
+
return () => {
|
|
5
|
+
const frame = (ts) => {
|
|
6
|
+
tick(ts);
|
|
7
|
+
rafId = requestAnimationFrame(frame);
|
|
8
|
+
};
|
|
9
|
+
rafId = requestAnimationFrame(frame);
|
|
10
|
+
return () => cancelAnimationFrame(rafId);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const createIntervalDriver = (tick) => {
|
|
14
|
+
const timeSource = globalThis.performance || globalThis.Date;
|
|
15
|
+
const tickTime = () => tick(timeSource.now());
|
|
16
|
+
return () => {
|
|
17
|
+
const intervalId = setInterval(tickTime, 1000 / default_interval_fps);
|
|
18
|
+
return () => clearInterval(intervalId);
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export const masterDriver = (() => {
|
|
22
|
+
const timelines = new Map();
|
|
23
|
+
let previousTime = null;
|
|
24
|
+
let pause = null;
|
|
25
|
+
const stepAll = (currentTime) => {
|
|
26
|
+
if (previousTime === null) {
|
|
27
|
+
previousTime = currentTime;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const delta = currentTime - previousTime;
|
|
31
|
+
previousTime = currentTime;
|
|
32
|
+
timelines.forEach((step, tl) => {
|
|
33
|
+
step(delta);
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
const start = "requestAnimationFrame" in globalThis
|
|
37
|
+
? createRafDriver(stepAll)
|
|
38
|
+
: createIntervalDriver(stepAll);
|
|
39
|
+
return (stepFn) => {
|
|
40
|
+
const key = Symbol();
|
|
41
|
+
timelines.set(key, stepFn);
|
|
42
|
+
if (timelines.size === 1) {
|
|
43
|
+
previousTime = null;
|
|
44
|
+
pause = start();
|
|
45
|
+
}
|
|
46
|
+
return () => {
|
|
47
|
+
timelines.delete(key);
|
|
48
|
+
if (timelines.size === 0) {
|
|
49
|
+
pause();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
})();
|
package/internal/emitters.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type ListenFunc<T> = (handler: Handler<T>) => UnsubscribeFunc;
|
|
|
6
6
|
export type UnsubscribeFunc = () => void;
|
|
7
7
|
export declare class Emitter<T> {
|
|
8
8
|
protected onListen: ListenFunc<T>;
|
|
9
|
-
|
|
9
|
+
constructor(onListen: ListenFunc<T>);
|
|
10
10
|
protected transform<R = T>(handler: (value: T, emit: (value: R) => void) => void): (fn: (v: R) => void) => UnsubscribeFunc;
|
|
11
11
|
/**
|
|
12
12
|
* Compatibility alias for `apply()` - registers a function to receive emitted values
|
|
@@ -41,7 +41,7 @@ export declare class Emitter<T> {
|
|
|
41
41
|
*/
|
|
42
42
|
dedupe(compare?: (a: T, b: T) => boolean): Emitter<T>;
|
|
43
43
|
/**
|
|
44
|
-
* Creates a chainable emitter that
|
|
44
|
+
* Creates a chainable emitter that forwards emissions from the parent emitter, invoking the provided callback `cb` as a side effect for each emission.
|
|
45
45
|
*
|
|
46
46
|
* The callback `cb` is called exactly once per parent emission, regardless of how many listeners are attached to the returned emitter.
|
|
47
47
|
* All listeners attached to the returned emitter receive the same values as the parent emitter.
|
|
@@ -70,6 +70,12 @@ export declare class Emitter<T> {
|
|
|
70
70
|
* @param cb
|
|
71
71
|
*/
|
|
72
72
|
fork(...cb: ((branch: this) => void)[]): this;
|
|
73
|
+
/**
|
|
74
|
+
* Creates a chainable emitter that forwards emissions from the parent and any of the provided emitters
|
|
75
|
+
* @param emitters
|
|
76
|
+
*/
|
|
77
|
+
or(...emitters: Emitter<T>[]): Emitter<T>;
|
|
78
|
+
or<U>(...emitters: Emitter<U>[]): Emitter<T | U>;
|
|
73
79
|
}
|
|
74
80
|
export declare class RangeProgression extends Emitter<number> {
|
|
75
81
|
/**
|
package/internal/emitters.js
CHANGED
|
@@ -66,7 +66,7 @@ export class Emitter {
|
|
|
66
66
|
return new Emitter(listen);
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* Creates a chainable emitter that
|
|
69
|
+
* Creates a chainable emitter that forwards emissions from the parent emitter, invoking the provided callback `cb` as a side effect for each emission.
|
|
70
70
|
*
|
|
71
71
|
* The callback `cb` is called exactly once per parent emission, regardless of how many listeners are attached to the returned emitter.
|
|
72
72
|
* All listeners attached to the returned emitter receive the same values as the parent emitter.
|
|
@@ -104,6 +104,12 @@ export class Emitter {
|
|
|
104
104
|
cb.forEach(cb => cb(this));
|
|
105
105
|
return this;
|
|
106
106
|
}
|
|
107
|
+
or(...emitters) {
|
|
108
|
+
return new Emitter(handler => {
|
|
109
|
+
const unsubs = [this, ...emitters].map(e => e.listen(handler));
|
|
110
|
+
return () => unsubs.forEach(unsub => unsub());
|
|
111
|
+
});
|
|
112
|
+
}
|
|
107
113
|
}
|
|
108
114
|
export class RangeProgression extends Emitter {
|
|
109
115
|
ease(easer) {
|
package/internal/timeline.d.ts
CHANGED
|
@@ -3,31 +3,13 @@ import { RangeProgression, UnsubscribeFunc } from "./emitters.js";
|
|
|
3
3
|
import { TimelinePoint } from "./point.js";
|
|
4
4
|
import { TimelineRange } from "./range.js";
|
|
5
5
|
import { Tweenable } from "./tween.js";
|
|
6
|
-
import { Widen } from "./utils.js";
|
|
6
|
+
import { Period, Widen } from "./utils.js";
|
|
7
7
|
declare const EndAction: {
|
|
8
8
|
readonly pause: 0;
|
|
9
9
|
readonly continue: 1;
|
|
10
10
|
readonly wrap: 2;
|
|
11
11
|
readonly restart: 3;
|
|
12
12
|
};
|
|
13
|
-
type Period = {
|
|
14
|
-
asMilliseconds: number;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Creates an autoplaying Timeline and returns a range from it
|
|
18
|
-
* @param durationMs Animation duration, in milliseconds
|
|
19
|
-
* @returns Object representing a range on a single-use, autoplaying Timeline
|
|
20
|
-
*/
|
|
21
|
-
export declare function animate(durationMs: number): TimelineRange;
|
|
22
|
-
export declare function animate(period: Period): TimelineRange;
|
|
23
|
-
/**
|
|
24
|
-
* Creates a looping Timeline and returns a range from it
|
|
25
|
-
*
|
|
26
|
-
* This timeline will play while it has active listeners
|
|
27
|
-
* @param duration Animation duration, in milliseconds, or a Period
|
|
28
|
-
* @returns Object representing a range on a single-use, autoplaying Timeline
|
|
29
|
-
*/
|
|
30
|
-
export declare function animate(duration: number | Period, looping: true): TimelineRange;
|
|
31
13
|
type TimelineOptions = {
|
|
32
14
|
atEnd?: {
|
|
33
15
|
wrapAt: number;
|
|
@@ -198,6 +180,7 @@ export declare class Timeline {
|
|
|
198
180
|
* @param from Value at start of range
|
|
199
181
|
* @param to Value at end of range
|
|
200
182
|
* @param easer Optional easing function
|
|
183
|
+
* @deprecated Legacy API may be absent in a future major version
|
|
201
184
|
*/
|
|
202
185
|
tween<T extends Tweenable>(start: number | TimelinePoint, duration: number, apply: (v: Widen<T>) => void, from: T, to: T, easer?: Easer | keyof typeof easers): ChainingInterface;
|
|
203
186
|
tween<T extends Tweenable>(start: number | TimelinePoint, end: TimelinePoint, // ease migration for tl.tween(0, tl.end, ...)
|
|
@@ -210,6 +193,7 @@ export declare class Timeline {
|
|
|
210
193
|
* @param action Handler for forward seeking
|
|
211
194
|
* @param reverse Handler for backward seeking
|
|
212
195
|
* @returns A tween/event chaining interface
|
|
196
|
+
* @deprecated Legacy API may be absent in a future major version
|
|
213
197
|
*/
|
|
214
198
|
at(position: number | TimelinePoint, action?: () => void, reverse?: boolean | (() => void)): ChainingInterface;
|
|
215
199
|
private createChainingInterface;
|
|
@@ -218,6 +202,9 @@ export declare class Timeline {
|
|
|
218
202
|
*/
|
|
219
203
|
get position(): number;
|
|
220
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* @deprecated Legacy API may be absent in a future major version
|
|
207
|
+
*/
|
|
221
208
|
export interface ChainingInterface {
|
|
222
209
|
thenTween<T extends Tweenable>(duration: number, apply: (v: Widen<T>) => void, from: T, to: T, easer?: Easer): ChainingInterface;
|
|
223
210
|
then(action: () => void): ChainingInterface;
|
package/internal/timeline.js
CHANGED
|
@@ -2,89 +2,13 @@ import { createListenable, RangeProgression } from "./emitters.js";
|
|
|
2
2
|
import { TimelinePoint } from "./point.js";
|
|
3
3
|
import { TimelineRange } from "./range.js";
|
|
4
4
|
import { clamp } from "./utils.js";
|
|
5
|
-
|
|
6
|
-
const createRafDriver = (tick) => {
|
|
7
|
-
let rafId = null;
|
|
8
|
-
return () => {
|
|
9
|
-
const frame = (ts) => {
|
|
10
|
-
tick(ts);
|
|
11
|
-
rafId = requestAnimationFrame(frame);
|
|
12
|
-
};
|
|
13
|
-
rafId = requestAnimationFrame(frame);
|
|
14
|
-
return () => cancelAnimationFrame(rafId);
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
const createIntervalDriver = (tick) => {
|
|
18
|
-
return () => {
|
|
19
|
-
const intervalId = setInterval(() => tick(performance.now()), 1000 / default_interval_fps);
|
|
20
|
-
return () => clearInterval(intervalId);
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
const masterDriver = (() => {
|
|
24
|
-
const timelines = new Map();
|
|
25
|
-
let previousTime = null;
|
|
26
|
-
let pause = null;
|
|
27
|
-
const step = (currentTime) => {
|
|
28
|
-
if (previousTime === null) {
|
|
29
|
-
previousTime = currentTime;
|
|
30
|
-
}
|
|
31
|
-
const delta = currentTime - previousTime;
|
|
32
|
-
previousTime = currentTime;
|
|
33
|
-
timelines.forEach((step, tl) => {
|
|
34
|
-
step(delta * tl.timeScale);
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
const start = "requestAnimationFrame" in globalThis
|
|
38
|
-
? createRafDriver(step)
|
|
39
|
-
: createIntervalDriver(step);
|
|
40
|
-
return {
|
|
41
|
-
add: (timeline, stepFn) => {
|
|
42
|
-
timelines.set(timeline, stepFn);
|
|
43
|
-
if (timelines.size === 1) {
|
|
44
|
-
previousTime = null;
|
|
45
|
-
pause = start();
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
remove: (timeline) => {
|
|
49
|
-
timelines.delete(timeline);
|
|
50
|
-
if (timelines.size === 0) {
|
|
51
|
-
pause();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
})();
|
|
5
|
+
import { masterDriver } from "./driver.js";
|
|
56
6
|
const EndAction = {
|
|
57
7
|
pause: 0,
|
|
58
8
|
continue: 1,
|
|
59
9
|
wrap: 2,
|
|
60
10
|
restart: 3,
|
|
61
11
|
};
|
|
62
|
-
export function animate(duration, looping = false) {
|
|
63
|
-
const tl = new Timeline(false, looping ? "wrap" : "pause");
|
|
64
|
-
const durationMs = typeof duration == "number"
|
|
65
|
-
? duration
|
|
66
|
-
: duration.asMilliseconds;
|
|
67
|
-
const parentRange = tl.range(0, durationMs).ease();
|
|
68
|
-
if (looping) {
|
|
69
|
-
let listeners = 0;
|
|
70
|
-
const range = new TimelineRange(h => {
|
|
71
|
-
if (++listeners == 1) {
|
|
72
|
-
tl.play();
|
|
73
|
-
}
|
|
74
|
-
let unsub = parentRange.apply(h);
|
|
75
|
-
return () => {
|
|
76
|
-
if (--listeners == 0)
|
|
77
|
-
tl.pause();
|
|
78
|
-
unsub();
|
|
79
|
-
};
|
|
80
|
-
}, tl, tl.start, tl.point(durationMs));
|
|
81
|
-
return range;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
tl.play();
|
|
85
|
-
return parentRange;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
12
|
export class Timeline {
|
|
89
13
|
/**
|
|
90
14
|
* The current position of this Timeline's 'play head'
|
|
@@ -411,8 +335,7 @@ export class Timeline {
|
|
|
411
335
|
this.seek(arg.start);
|
|
412
336
|
return this.seek(arg.end, arg.duration / this.timeScale, easer);
|
|
413
337
|
}
|
|
414
|
-
masterDriver
|
|
415
|
-
this._pause = () => masterDriver.remove(this);
|
|
338
|
+
this._pause = masterDriver(n => this.next(n * this.timeScale));
|
|
416
339
|
}
|
|
417
340
|
next(delta) {
|
|
418
341
|
if (this._currentTime + delta <= this._endPosition) {
|
|
@@ -481,6 +404,7 @@ export class Timeline {
|
|
|
481
404
|
* @param action Handler for forward seeking
|
|
482
405
|
* @param reverse Handler for backward seeking
|
|
483
406
|
* @returns A tween/event chaining interface
|
|
407
|
+
* @deprecated Legacy API may be absent in a future major version
|
|
484
408
|
*/
|
|
485
409
|
at(position, action, reverse) {
|
|
486
410
|
const point = typeof position == "number" ? this.point(position) : position;
|
package/internal/utils.d.ts
CHANGED