@xtia/timeline 1.2.2 → 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 CHANGED
@@ -1,5 +1,6 @@
1
- export { Timeline, animate, ChainingInterface } from "./internal/timeline.js";
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, animate } from "./internal/timeline.js";
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
+ })();
@@ -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
- protected constructor(onListen: ListenFunc<T>);
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
@@ -3,29 +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 one-shot progression emitter
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): RangeProgression;
22
- export declare function animate(period: Period): RangeProgression;
23
- /**
24
- * Creates a looping progression emitter that will play while it has active listeners
25
- * @param duration Animation duration, in milliseconds, or a Period
26
- * @returns Object representing a range on a looping Timeline
27
- */
28
- export declare function animate(duration: number | Period, looping: true): RangeProgression;
29
13
  type TimelineOptions = {
30
14
  atEnd?: {
31
15
  wrapAt: number;
@@ -196,6 +180,7 @@ export declare class Timeline {
196
180
  * @param from Value at start of range
197
181
  * @param to Value at end of range
198
182
  * @param easer Optional easing function
183
+ * @deprecated Legacy API may be absent in a future major version
199
184
  */
200
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;
201
186
  tween<T extends Tweenable>(start: number | TimelinePoint, end: TimelinePoint, // ease migration for tl.tween(0, tl.end, ...)
@@ -208,6 +193,7 @@ export declare class Timeline {
208
193
  * @param action Handler for forward seeking
209
194
  * @param reverse Handler for backward seeking
210
195
  * @returns A tween/event chaining interface
196
+ * @deprecated Legacy API may be absent in a future major version
211
197
  */
212
198
  at(position: number | TimelinePoint, action?: () => void, reverse?: boolean | (() => void)): ChainingInterface;
213
199
  private createChainingInterface;
@@ -216,6 +202,9 @@ export declare class Timeline {
216
202
  */
217
203
  get position(): number;
218
204
  }
205
+ /**
206
+ * @deprecated Legacy API may be absent in a future major version
207
+ */
219
208
  export interface ChainingInterface {
220
209
  thenTween<T extends Tweenable>(duration: number, apply: (v: Widen<T>) => void, from: T, to: T, easer?: Easer): ChainingInterface;
221
210
  then(action: () => void): ChainingInterface;
@@ -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
- const default_interval_fps = 60;
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
- const timeSource = globalThis.performance || globalThis.Date;
19
- const tickTime = () => tick(timeSource.now());
20
- return () => {
21
- const intervalId = setInterval(tickTime, 1000 / default_interval_fps);
22
- return () => clearInterval(intervalId);
23
- };
24
- };
25
- const masterDriver = (() => {
26
- const timelines = new Map();
27
- let previousTime = null;
28
- let pause = null;
29
- const stepAll = (currentTime) => {
30
- if (previousTime === null) {
31
- previousTime = currentTime;
32
- return;
33
- }
34
- const delta = currentTime - previousTime;
35
- previousTime = currentTime;
36
- timelines.forEach((step, tl) => {
37
- step(delta * tl.timeScale);
38
- });
39
- };
40
- const start = "requestAnimationFrame" in globalThis
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 () => {
50
- timelines.delete(timeline);
51
- if (timelines.size === 0) {
52
- pause();
53
- }
54
- };
55
- };
56
- })();
5
+ import { masterDriver } from "./driver.js";
57
6
  const EndAction = {
58
7
  pause: 0,
59
8
  continue: 1,
60
9
  wrap: 2,
61
10
  restart: 3,
62
11
  };
63
- export function animate(duration, looping = false) {
64
- const tl = new Timeline(!looping, looping ? "wrap" : "pause");
65
- const durationMs = typeof duration == "number"
66
- ? duration
67
- : duration.asMilliseconds;
68
- const parentRange = tl.range(0, durationMs);
69
- if (looping) {
70
- let listeners = 0;
71
- const range = new TimelineRange(h => {
72
- if (++listeners == 1) {
73
- tl.play();
74
- }
75
- let unsub = parentRange.apply(h);
76
- return () => {
77
- if (--listeners == 0)
78
- tl.pause();
79
- unsub();
80
- };
81
- }, tl, tl.start, tl.point(durationMs));
82
- return range.ease(); // empty ease() to coax TimelineRange -> RangeProgression
83
- }
84
- else {
85
- return parentRange.ease();
86
- }
87
- }
88
12
  export class Timeline {
89
13
  /**
90
14
  * The current position of this Timeline's 'play head'
@@ -411,7 +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
- this._pause = masterDriver(this, n => this.next(n));
338
+ this._pause = masterDriver(n => this.next(n * this.timeScale));
415
339
  }
416
340
  next(delta) {
417
341
  if (this._currentTime + delta <= this._endPosition) {
@@ -480,6 +404,7 @@ export class Timeline {
480
404
  * @param action Handler for forward seeking
481
405
  * @param reverse Handler for backward seeking
482
406
  * @returns A tween/event chaining interface
407
+ * @deprecated Legacy API may be absent in a future major version
483
408
  */
484
409
  at(position, action, reverse) {
485
410
  const point = typeof position == "number" ? this.point(position) : position;
@@ -2,3 +2,6 @@
2
2
  export declare const clamp: (value: number, min?: number, max?: number) => number;
3
3
  /** @internal */
4
4
  export type Widen<T> = T extends number ? number : T extends string ? string : T;
5
+ export type Period = {
6
+ asMilliseconds: number;
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/timeline",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/timeline",
6
6
  "type": "github"