@xtia/timeline 1.0.12 → 1.1.0

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 CHANGED
@@ -21,7 +21,7 @@ const timeline = new Timeline();
21
21
  timeline
22
22
  .range(0, 1000)
23
23
  .tween("#646", "#000")
24
- .listen(
24
+ .apply(
25
25
  value => element.style.background = value
26
26
  );
27
27
 
@@ -31,7 +31,7 @@ timeline
31
31
  .range(500, 2000)
32
32
  .tween(0, message.length)
33
33
  .map(n => message.substring(0, n))
34
- .listen(
34
+ .apply(
35
35
  s => element.textContent = s
36
36
  );
37
37
 
@@ -40,7 +40,7 @@ timeline
40
40
  .range(0, 3000)
41
41
  .ease("bounce")
42
42
  .tween("50%", "0%")
43
- .listen(
43
+ .apply(
44
44
  value => element.style.marginLeft = value
45
45
  );
46
46
 
@@ -56,11 +56,11 @@ timeline.play();
56
56
  const firstFiveSeconds = timeline.range(0, 5000);
57
57
  ```
58
58
 
59
- The range object is *listenable* and emits a progression value (between 0 and 1) when the Timeline's internal position passes through or over that period.
59
+ The range object is *applyable* and emits a progression value (between 0 and 1) when the Timeline's internal position passes through or over that period.
60
60
 
61
61
  ```ts
62
62
  firstFiveSeconds
63
- .listen(
63
+ .apply(
64
64
  value => console.log(`${value} is between 0 and 1`)
65
65
  );
66
66
  ```
@@ -74,20 +74,20 @@ const asPercent = firstFiveSeconds.map(n => n * 100);
74
74
  // use the result in a log message
75
75
  asPercent
76
76
  .map(n => n.toFixed(2))
77
- .listen(
77
+ .apply(
78
78
  n => console.log(`We are ${n}% through the first five seconds`)
79
79
  );
80
80
 
81
81
  // and in a css property
82
82
  asPercent
83
83
  .map(n => `${n}%`)
84
- .listen(
84
+ .apply(
85
85
  n => progressBar.style.width = n
86
86
  );
87
87
 
88
88
  // apply easing
89
89
  const eased = firstFiveSeconds.ease("easeInOut");
90
- eased.listen(
90
+ eased.apply(
91
91
  v => console.log(`Eased value: ${v}`)
92
92
  );
93
93
 
@@ -98,7 +98,7 @@ range
98
98
  .dedupe()
99
99
  .tap(n => console.log("Showing frame #", n))
100
100
  .map(n => `animation-frame-${n}.png`)
101
- .listen(filename => img.src = filename);
101
+ .apply(filename => img.src = filename);
102
102
 
103
103
  // each step in a chain is a 'pure', independent emitter that emits a
104
104
  // transformation of its parent's emissions
@@ -146,7 +146,7 @@ const sixSecondsIn = fiveSecondsdIn.delta(1000);
146
146
  Points emit `PointEvent` objects when their position is reached or passed.
147
147
 
148
148
  ```ts
149
- twoSecondsIn.listen(event => {
149
+ twoSecondsIn.apply(event => {
150
150
  // event.direction (-1 | 1) tells us the direction of the seek that
151
151
  // triggered the point. This allows for reversible point events
152
152
  document.body.classList.toggle("someClass", event.direction > 0);
@@ -181,30 +181,30 @@ const range = timeline.range(0, 2000);
181
181
  range
182
182
  .ease("overshootIn")
183
183
  .tween(300, 500)
184
- .listen(v => element.scrollTop = v);
184
+ .apply(v => element.scrollTop = v);
185
185
 
186
186
  // number arrays
187
187
  range
188
188
  .tween([0, 180], [360, 180])
189
- .listen((angles) => pieChart.setValues(angles));
189
+ .apply((angles) => pieChart.setValues(angles));
190
190
 
191
191
  // strings
192
192
  range
193
193
  .tween("#000000", "#ff00ff")
194
- .listen(v => element.style.color = v);
194
+ .apply(v => element.style.color = v);
195
195
 
196
196
  // blendable objects
197
197
  // (T extends { blend(from: this, to: this): this })
198
198
  import { RGBA } from "@xtia/rgba";
199
199
  range
200
200
  .tween(RGBA.parse("#c971a7"), RGBA.parse("#fff"))
201
- .listen(v => element.style.background = v);
201
+ .apply(v => element.style.background = v);
202
202
 
203
203
  import { Angle } from "@xtia/mezr";
204
204
  range
205
205
  .tween(Angle.degrees(45), Angle.turns(.5))
206
206
  .map(a => `rotate(${a.asDegrees}deg)`)
207
- .listen(v => element.style.transform = v);
207
+ .apply(v => element.style.transform = v);
208
208
 
209
209
  ```
210
210
 
@@ -218,14 +218,14 @@ timeline
218
218
  .range(0, 2000)
219
219
  .ease("elastic")
220
220
  .tween("0px 0px 0px #0000", "15px 15px 20px #0005")
221
- .listen(s => element.style.textShadow = s);
221
+ .apply(s => element.style.textShadow = s);
222
222
 
223
223
  // text progress bar
224
224
  timeline
225
225
  .range(0, 2000)
226
226
  .tween("--------", "########")
227
227
  .dedupe()
228
- .listen(v => document.title = v);
228
+ .apply(v => document.title = v);
229
229
  ```
230
230
 
231
231
  Try out the [shadow tweening example at StackBlitz](https://stackblitz.com/edit/timeline-string-tween?file=src%2Fmain.ts)
@@ -238,13 +238,13 @@ To create a Timeline that immediately starts playing, pass `true` to its constru
238
238
  // immediately fade in an element
239
239
  new Timeline(true)
240
240
  .range(0, 1000)
241
- .listen(v => element.style.opacity = v);
241
+ .apply(v => element.style.opacity = v);
242
242
 
243
243
  // note, an `animate(duration)` function is exported for
244
244
  // disposable, single-use animations such as this:
245
245
  import { animate } from "@xtia/timeline";
246
246
  animate(1000)
247
- .listen(v => element.style.opacity = v);
247
+ .apply(v => element.style.opacity = v);
248
248
  ```
249
249
 
250
250
  Normally a Timeline will simply stop playing when it reaches the end. This can be changed by passing a second argument (`endAction`) to the constructor.
@@ -302,19 +302,19 @@ window.addEventListener(
302
302
  setInterval(() => timeline.seek(Date.now()), 1000);
303
303
  timeline
304
304
  .point(new Date("2026-10-31").getTime())
305
- .listen(() => console.log("Happy anniversary 🏳️‍⚧️💗"));
305
+ .apply(() => console.log("Happy anniversary 🏳️‍⚧️💗"));
306
306
 
307
307
  // show a progress bar for loaded resources
308
308
  const loadingTimeline = new Timeline();
309
309
  loadingTimeline
310
310
  .range(0, resourceUrls.length)
311
311
  .tween("0%", "100%");
312
- .listen(v => progressBar.style.width = v);
312
+ .apply(v => progressBar.style.width = v);
313
313
 
314
314
  // and do something when they're loaded
315
315
  loadingTimeline
316
316
  .end
317
- .listen(startGame);
317
+ .apply(startGame);
318
318
 
319
319
  // to drive it, just seek forward by 1 for each loaded resource
320
320
  resourceUrls.forEach(url => {
@@ -435,7 +435,7 @@ timeline
435
435
  .range(start, duration)
436
436
  .ease(easer)
437
437
  .tween(from, to)
438
- .listen(apply);
438
+ .apply(apply);
439
439
  ```
440
440
 
441
441
  Returns a [`ChainingInterface`](#chaininginterface-interface) representing the point at which the tween ends.
@@ -508,7 +508,7 @@ Allows point listeners to undo effects when the Timeline is reversed.
508
508
  ```ts
509
509
  timeline
510
510
  .point(4000)
511
- .listen(
511
+ .apply(
512
512
  event => element.classList.toggle(
513
513
  "visible",
514
514
  event.direction > 0
@@ -650,9 +650,9 @@ range
650
650
  .fork(branch => {
651
651
  branch
652
652
  .map(s => `Loading: ${s}`)
653
- .listen(s => document.title = s)
653
+ .apply(s => document.title = s)
654
654
  })
655
- .listen(v => progressBar.style.width = v);
655
+ .apply(v => progressBar.style.width = v);
656
656
  ```
657
657
 
658
658
 
@@ -662,7 +662,7 @@ range
662
662
 
663
663
  #### Methods
664
664
 
665
- ##### `listen(handler: Handler<T>): UnsubscribeFunc`
665
+ ##### `apply(handler: Handler<T>): UnsubscribeFunc`
666
666
 
667
667
  Attaches a handler to the emitter and returns a function that will unsubscribe the handler.
668
668
 
@@ -696,9 +696,9 @@ range
696
696
  .fork(branch => {
697
697
  branch
698
698
  .map(s => `Loading: ${s}`)
699
- .listen(s => document.title = s)
699
+ .apply(s => document.title = s)
700
700
  })
701
- .listen(v => progressBar.style.width = v);
701
+ .apply(v => progressBar.style.width = v);
702
702
  ```
703
703
 
704
704
 
@@ -16,11 +16,17 @@ export declare class Emitter<T> {
16
16
  */
17
17
  protected redirect: (listen: ListenFunc<T>) => Emitter<T>;
18
18
  /**
19
- * Registers a function to receive emitted values
19
+ * Compatibility alias for `apply()` - registers a function to receive emitted values
20
20
  * @param handler
21
21
  * @returns A function to deregister the handler
22
22
  */
23
23
  listen(handler: Handler<T>): UnsubscribeFunc;
24
+ /**
25
+ * Registers a function to receive emitted values
26
+ * @param handler
27
+ * @returns A function to deregister the handler
28
+ */
29
+ apply(handler: Handler<T>): UnsubscribeFunc;
24
30
  /**
25
31
  * Creates a chainable emitter that applies arbitrary transformation to values emitted by its parent
26
32
  * @param mapFunc
@@ -65,9 +71,9 @@ export declare class Emitter<T> {
65
71
  * .fork(branch => {
66
72
  * branch
67
73
  * .map(s => `Loading: ${s}`)
68
- * .listen(s => document.title = s)
74
+ * .apply(s => document.title = s)
69
75
  * })
70
- * .listen(v => progressBar.style.width = v);
76
+ * .apply(v => progressBar.style.width = v);
71
77
  * ```
72
78
  * @param cb
73
79
  */
@@ -106,7 +112,7 @@ export declare class RangeProgression extends Emitter<number> {
106
112
  * ```ts
107
113
  * range
108
114
  * .tween("0px 0px 0px #0000", "4px 4px 8px #0005")
109
- * .listen(s => element.style.textShadow = s);
115
+ * .apply(s => element.style.textShadow = s);
110
116
  * ```
111
117
  *
112
118
  * @param from Value to interpolate from
@@ -132,7 +138,7 @@ export declare class RangeProgression extends Emitter<number> {
132
138
  * ```ts
133
139
  * range
134
140
  * .sample(["a", "b", "c"])
135
- * .listen(v => console.log(v));
141
+ * .apply(v => console.log(v));
136
142
  * // logs 'b' when a seek lands halfway through range
137
143
  * ```
138
144
  * @param source array to sample
@@ -15,7 +15,7 @@ export class Emitter {
15
15
  this.redirect = (listen) => new Emitter(listen);
16
16
  }
17
17
  /**
18
- * Registers a function to receive emitted values
18
+ * Compatibility alias for `apply()` - registers a function to receive emitted values
19
19
  * @param handler
20
20
  * @returns A function to deregister the handler
21
21
  */
@@ -24,6 +24,16 @@ export class Emitter {
24
24
  handler(value);
25
25
  });
26
26
  }
27
+ /**
28
+ * Registers a function to receive emitted values
29
+ * @param handler
30
+ * @returns A function to deregister the handler
31
+ */
32
+ apply(handler) {
33
+ return this.onListen((value) => {
34
+ handler(value);
35
+ });
36
+ }
27
37
  /**
28
38
  * Creates a chainable emitter that applies arbitrary transformation to values emitted by its parent
29
39
  * @param mapFunc
@@ -111,9 +121,9 @@ export class Emitter {
111
121
  * .fork(branch => {
112
122
  * branch
113
123
  * .map(s => `Loading: ${s}`)
114
- * .listen(s => document.title = s)
124
+ * .apply(s => document.title = s)
115
125
  * })
116
- * .listen(v => progressBar.style.width = v);
126
+ * .apply(v => progressBar.style.width = v);
117
127
  * ```
118
128
  * @param cb
119
129
  */
@@ -148,7 +158,7 @@ export class RangeProgression extends Emitter {
148
158
  * ```ts
149
159
  * range
150
160
  * .sample(["a", "b", "c"])
151
- * .listen(v => console.log(v));
161
+ * .apply(v => console.log(v));
152
162
  * // logs 'b' when a seek lands halfway through range
153
163
  * ```
154
164
  * @param source array to sample
@@ -174,7 +184,7 @@ export class RangeProgression extends Emitter {
174
184
  }
175
185
  return new RangeProgression(handler => this.onListen(progress => {
176
186
  const snapped = Math.round(progress * steps) / steps;
177
- handler(clamp(snapped));
187
+ handler(snapped);
178
188
  }));
179
189
  }
180
190
  /**
@@ -32,8 +32,22 @@ export declare class TimelineRange extends RangeProgression {
32
32
  */
33
33
  spread(count: number): TimelinePoint[];
34
34
  /**
35
- * Progresses the Timeline across the range
36
- * @param easer
35
+ * Creates the specified number of ranges, each of `(parent.duration / count)` duration, spread
36
+ * evenly over this range
37
+ * @param count Number of sub-ranges to create
38
+ * @returns Array of sub-ranges
39
+ */
40
+ subdivide(count: number): TimelineRange[];
41
+ /**
42
+ * Creates a new range by offsetting the parent by a given time delta
43
+ * @param delta
44
+ * @returns Offset range
45
+ */
46
+ shift(delta: number): TimelineRange;
47
+ /**
48
+ * Progresses the Timeline across the range at 1000 units per second
49
+ * @param easer Optional easing function
50
+ * @returns Promise, resolved when the end is reached
37
51
  */
38
52
  play(easer?: Easer | keyof typeof easers): Promise<void>;
39
53
  /**
package/internal/range.js CHANGED
@@ -44,8 +44,27 @@ export class TimelineRange extends RangeProgression {
44
44
  ];
45
45
  }
46
46
  /**
47
- * Progresses the Timeline across the range
48
- * @param easer
47
+ * Creates the specified number of ranges, each of `(parent.duration / count)` duration, spread
48
+ * evenly over this range
49
+ * @param count Number of sub-ranges to create
50
+ * @returns Array of sub-ranges
51
+ */
52
+ subdivide(count) {
53
+ const duration = this.duration / count;
54
+ return Array.from({ length: count }, (_, i) => this.timeline.range(this.startPosition + i * duration, duration));
55
+ }
56
+ /**
57
+ * Creates a new range by offsetting the parent by a given time delta
58
+ * @param delta
59
+ * @returns Offset range
60
+ */
61
+ shift(delta) {
62
+ return this.timeline.range(this.startPosition + delta, this.duration);
63
+ }
64
+ /**
65
+ * Progresses the Timeline across the range at 1000 units per second
66
+ * @param easer Optional easing function
67
+ * @returns Promise, resolved when the end is reached
49
68
  */
50
69
  play(easer) {
51
70
  this.timeline.pause();
@@ -78,7 +97,7 @@ export class TimelineRange extends RangeProgression {
78
97
  */
79
98
  scale(factor, anchor = 0) {
80
99
  if (factor <= 0) {
81
- throw new RangeError('scale factor must be > 0');
100
+ throw new RangeError('Scale factor must be > 0');
82
101
  }
83
102
  const clampedAnchor = clamp(anchor, 0, 1);
84
103
  const oldLen = this.endPosition - this.startPosition;
@@ -184,7 +184,7 @@ export class Timeline {
184
184
  if (this.smoothSeeker !== null) {
185
185
  this.smoothSeeker.pause();
186
186
  // ensure any awaits are resolved for the previous seek
187
- this.smoothSeeker.seek(this.smoothSeeker.end);
187
+ this.smoothSeeker.end.seek();
188
188
  this.smoothSeeker = null;
189
189
  }
190
190
  if (duration === 0) {
@@ -193,8 +193,8 @@ export class Timeline {
193
193
  }
194
194
  const seeker = new Timeline(true);
195
195
  this.smoothSeeker = seeker;
196
- seeker.range(0, duration).ease(easer).tween(this.currentTime, toPosition).listen(v => this.seekDirect(v));
197
- return new Promise(r => seeker.end.listen(() => r()));
196
+ seeker.range(0, duration).ease(easer).tween(this.currentTime, toPosition).apply(v => this.seekDirect(v));
197
+ return new Promise(r => seeker.end.apply(() => r()));
198
198
  }
199
199
  seekDirect(toPosition) {
200
200
  const fromPosition = this._currentTime;
@@ -321,7 +321,7 @@ export class Timeline {
321
321
  const duration = typeof durationOrToPoint == "number"
322
322
  ? durationOrToPoint
323
323
  : (durationOrToPoint.position - startPosition);
324
- this.range(startPosition, duration).ease(easer).tween(from, to).listen(apply);
324
+ this.range(startPosition, duration).ease(easer).tween(from, to).apply(apply);
325
325
  return this.createChainingInterface(startPosition + duration);
326
326
  }
327
327
  at(position, action, reverse) {
@@ -329,7 +329,7 @@ export class Timeline {
329
329
  if (reverse === true)
330
330
  reverse = action;
331
331
  if (action)
332
- point.listen(reverse
332
+ point.apply(reverse
333
333
  ? (event => event.direction < 0 ? reverse() : action)
334
334
  : action);
335
335
  return this.createChainingInterface(point.position);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/timeline",
3
- "version": "1.0.12",
3
+ "version": "1.1.0",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/timeline",
6
6
  "type": "github"