@xtia/timeline 1.2.0 → 1.2.2

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.
@@ -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 mirrors emissions from the parent emitter, invoking the provided callback `cb` as a side effect for each emission.
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
  /**
@@ -66,7 +66,7 @@ export class Emitter {
66
66
  return new Emitter(listen);
67
67
  }
68
68
  /**
69
- * Creates a chainable emitter that mirrors emissions from the parent emitter, invoking the provided callback `cb` as a side effect for each emission.
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) {
@@ -14,20 +14,18 @@ type Period = {
14
14
  asMilliseconds: number;
15
15
  };
16
16
  /**
17
- * Creates an autoplaying Timeline and returns a range from it
17
+ * Creates an autoplaying one-shot progression emitter
18
18
  * @param durationMs Animation duration, in milliseconds
19
19
  * @returns Object representing a range on a single-use, autoplaying Timeline
20
20
  */
21
- export declare function animate(durationMs: number): TimelineRange;
22
- export declare function animate(period: Period): TimelineRange;
21
+ export declare function animate(durationMs: number): RangeProgression;
22
+ export declare function animate(period: Period): RangeProgression;
23
23
  /**
24
- * Creates a looping Timeline and returns a range from it
25
- *
26
- * This timeline will play while it has active listeners
24
+ * Creates a looping progression emitter that will play while it has active listeners
27
25
  * @param duration Animation duration, in milliseconds, or a Period
28
- * @returns Object representing a range on a single-use, autoplaying Timeline
26
+ * @returns Object representing a range on a looping Timeline
29
27
  */
30
- export declare function animate(duration: number | Period, looping: true): TimelineRange;
28
+ export declare function animate(duration: number | Period, looping: true): RangeProgression;
31
29
  type TimelineOptions = {
32
30
  atEnd?: {
33
31
  wrapAt: number;
@@ -15,8 +15,10 @@ const createRafDriver = (tick) => {
15
15
  };
16
16
  };
17
17
  const createIntervalDriver = (tick) => {
18
+ const timeSource = globalThis.performance || globalThis.Date;
19
+ const tickTime = () => tick(timeSource.now());
18
20
  return () => {
19
- const intervalId = setInterval(() => tick(performance.now()), 1000 / default_interval_fps);
21
+ const intervalId = setInterval(tickTime, 1000 / default_interval_fps);
20
22
  return () => clearInterval(intervalId);
21
23
  };
22
24
  };
@@ -24,9 +26,10 @@ const masterDriver = (() => {
24
26
  const timelines = new Map();
25
27
  let previousTime = null;
26
28
  let pause = null;
27
- const step = (currentTime) => {
29
+ const stepAll = (currentTime) => {
28
30
  if (previousTime === null) {
29
31
  previousTime = currentTime;
32
+ return;
30
33
  }
31
34
  const delta = currentTime - previousTime;
32
35
  previousTime = currentTime;
@@ -35,22 +38,20 @@ const masterDriver = (() => {
35
38
  });
36
39
  };
37
40
  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) => {
41
+ ? createRafDriver(stepAll)
42
+ : createIntervalDriver(stepAll);
43
+ return (timeline, stepFn) => {
44
+ timelines.set(timeline, stepFn);
45
+ if (timelines.size === 1) {
46
+ previousTime = null;
47
+ pause = start();
48
+ }
49
+ return () => {
49
50
  timelines.delete(timeline);
50
51
  if (timelines.size === 0) {
51
52
  pause();
52
53
  }
53
- }
54
+ };
54
55
  };
55
56
  })();
56
57
  const EndAction = {
@@ -60,11 +61,11 @@ const EndAction = {
60
61
  restart: 3,
61
62
  };
62
63
  export function animate(duration, looping = false) {
63
- const tl = new Timeline(false, "wrap");
64
+ const tl = new Timeline(!looping, looping ? "wrap" : "pause");
64
65
  const durationMs = typeof duration == "number"
65
66
  ? duration
66
67
  : duration.asMilliseconds;
67
- const parentRange = tl.range(0, durationMs).ease();
68
+ const parentRange = tl.range(0, durationMs);
68
69
  if (looping) {
69
70
  let listeners = 0;
70
71
  const range = new TimelineRange(h => {
@@ -78,11 +79,10 @@ export function animate(duration, looping = false) {
78
79
  unsub();
79
80
  };
80
81
  }, tl, tl.start, tl.point(durationMs));
81
- return range;
82
+ return range.ease(); // empty ease() to coax TimelineRange -> RangeProgression
82
83
  }
83
84
  else {
84
- tl.play();
85
- return parentRange;
85
+ return parentRange.ease();
86
86
  }
87
87
  }
88
88
  export class Timeline {
@@ -411,8 +411,7 @@ export class Timeline {
411
411
  this.seek(arg.start);
412
412
  return this.seek(arg.end, arg.duration / this.timeScale, easer);
413
413
  }
414
- masterDriver.add(this, n => this.next(n));
415
- this._pause = () => masterDriver.remove(this);
414
+ this._pause = masterDriver(this, n => this.next(n));
416
415
  }
417
416
  next(delta) {
418
417
  if (this._currentTime + delta <= this._endPosition) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/timeline",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/timeline",
6
6
  "type": "github"