@xtia/timeline 1.0.7 → 1.0.8

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
@@ -85,27 +85,41 @@ asPercent
85
85
  n => progressBar.style.width = n
86
86
  );
87
87
 
88
- // apply easing (creates a *new* emitter)
88
+ // apply easing
89
89
  const eased = firstFiveSeconds.ease("easeInOut");
90
90
  eased.listen(
91
91
  v => console.log(`Eased value: ${v}`)
92
92
  );
93
93
 
94
- // combine them
95
- const frames = eased
94
+ // chain them
95
+ range
96
96
  .tween(0, 30)
97
97
  .map(Math.floor)
98
98
  .dedupe()
99
99
  .tap(n => console.log("Showing frame #", n))
100
100
  .map(n => `animation-frame-${n}.png`)
101
101
  .listen(filename => img.src = filename);
102
+
103
+ // each step in the chain is a 'pure', independent emitter that emits
104
+ // a transformation of its parent's emissions
105
+ const filenameEmitter = range
106
+ .tween(0, 3)
107
+ .map(Math.floor)
108
+ .dedupe()
109
+ .map(n => `animation-frame-${n}.png`);
110
+
111
+ // filenameEmitter will emit filenames as the Timeline passes through 'range'.
112
+ // it can be listened directly or further transformed
113
+ const urlEmitter = filenameEmitter
114
+ .map(filename => `http://www.example.com/${filename}`);
115
+
102
116
  ```
103
117
 
104
118
  Range objects also provide a `play()` method that instructs the Timeline to play through that particular range:
105
119
 
106
120
  ```ts
107
121
  // play through the first two seconds of the Timeline
108
- timeline
122
+ await timeline
109
123
  .range(0, 2000)
110
124
  .play();
111
125
  ```
@@ -158,7 +172,41 @@ timeline
158
172
 
159
173
  ## More on tweening
160
174
 
161
- Tween emitters can interpolate numbers, arrays of numbers, strings, and objects with a method `blend(from: this, to: this): this`.
175
+ Tween emitters can interpolate numbers, arrays of numbers, strings, and objects with a method `blend(from: this, to: this): this`, by the progression value emitted by their parent.
176
+
177
+ ```ts
178
+ const range = timeline.range(0, 2000);
179
+
180
+ // numbers
181
+ range
182
+ .ease("overshootIn")
183
+ .tween(300, 500)
184
+ .listen(v => element.scrollTop = v);
185
+
186
+ // number arrays
187
+ range
188
+ .tween([0, 180], [360, 180])
189
+ .listen((angles) => pieChart.setValues(angles));
190
+
191
+ // strings
192
+ range
193
+ .tween("#000000", "#ff00ff")
194
+ .listen(v => element.style.color = v);
195
+
196
+ // blendable objects
197
+ // (T extends { blend(from: this, to: this): this })
198
+ import { RGBA } from "@xtia/rgba";
199
+ range
200
+ .tween(RGBA.parse("#c971a7"), RGBA.parse("#fff"))
201
+ .listen(v => element.style.background = v.hexCode);
202
+
203
+ import { Angle } from "@xtia/mezr";
204
+ range
205
+ .tween(Angle.degrees(45), Angle.turns(.5))
206
+ .map(a => `rotate(${a.asDegrees}deg)`)
207
+ .listen(v => element.style.transform = v);
208
+
209
+ ```
162
210
 
163
211
  #### String interpolation
164
212
  * If the strings contain tweenable tokens (numbers, colour codes) and are otherwise identical, those tokens are interpolated
@@ -180,7 +228,7 @@ timeline
180
228
  .listen(v => document.title = v);
181
229
  ```
182
230
 
183
- You can try out the [shadow tweening example at StackBlitz](https://stackblitz.com/edit/timeline-string-tween?file=src%2Fmain.ts)
231
+ Try out the [shadow tweening example at StackBlitz](https://stackblitz.com/edit/timeline-string-tween?file=src%2Fmain.ts)
184
232
 
185
233
  ## Autoplay and Looping Strategies
186
234
 
@@ -190,13 +238,13 @@ To create a Timeline that immediately starts playing, pass `true` to its constru
190
238
  // immediately fade in an element
191
239
  new Timeline(true)
192
240
  .range(0, 1000)
193
- .tween(v => element.style.opacity = v);
241
+ .listen(v => element.style.opacity = v);
194
242
 
195
243
  // note, an `animate(duration)` function is exported for
196
244
  // disposable, single-use animations such as this:
197
245
  import { animate } from "@xtia/timeline";
198
246
  animate(1000)
199
- .tween(v => element.style.opacity = v);
247
+ .listen(v => element.style.opacity = v);
200
248
  ```
201
249
 
202
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.
@@ -279,7 +327,7 @@ resourceUrls.forEach(url => {
279
327
  We can pass a second argument to `seek()` to perform a 'smooth seek' over the given duration. A third argument can provide an easing function for the smooth seek process:
280
328
 
281
329
  ```ts
282
- timeline.seek(timeline.end, 400, "overshootIn");
330
+ await timeline.seek(timeline.end, 400, "overshootIn");
283
331
  ```
284
332
 
285
333
  ## Backward-compatibility
package/index.js CHANGED
@@ -1,15 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.easers = exports.RangeProgression = exports.Emitter = exports.TimelineRange = exports.TimelinePoint = exports.animate = exports.Timeline = void 0;
4
- var timeline_1 = require("./internal/timeline");
5
- Object.defineProperty(exports, "Timeline", { enumerable: true, get: function () { return timeline_1.Timeline; } });
6
- Object.defineProperty(exports, "animate", { enumerable: true, get: function () { return timeline_1.animate; } });
7
- var point_1 = require("./internal/point");
8
- Object.defineProperty(exports, "TimelinePoint", { enumerable: true, get: function () { return point_1.TimelinePoint; } });
9
- var range_1 = require("./internal/range");
10
- Object.defineProperty(exports, "TimelineRange", { enumerable: true, get: function () { return range_1.TimelineRange; } });
11
- var emitters_1 = require("./internal/emitters");
12
- Object.defineProperty(exports, "Emitter", { enumerable: true, get: function () { return emitters_1.Emitter; } });
13
- Object.defineProperty(exports, "RangeProgression", { enumerable: true, get: function () { return emitters_1.RangeProgression; } });
14
- var easing_1 = require("./internal/easing");
15
- Object.defineProperty(exports, "easers", { enumerable: true, get: function () { return easing_1.easers; } });
1
+ export { Timeline, animate } from "./internal/timeline";
2
+ export { TimelinePoint } from "./internal/point";
3
+ export { TimelineRange } from "./internal/range";
4
+ export { Emitter, RangeProgression } from "./internal/emitters";
5
+ export { easers } from "./internal/easing";
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.easers = void 0;
4
1
  const overshoot = 1.70158;
5
- exports.easers = {
2
+ export const easers = {
6
3
  linear: (x) => x,
7
4
  easeIn: (x) => x * x,
8
5
  easeIn4: (x) => Math.pow(x, 4),
@@ -1,5 +1,5 @@
1
1
  import { Easer, easers } from "./easing";
2
- import { Tweenable } from "./tween";
2
+ import { BlendableWith, Tweenable } from "./tween";
3
3
  type Handler<T> = (value: T) => void;
4
4
  export type ListenFunc<T> = (handler: Handler<T>) => UnsubscribeFunc;
5
5
  export type UnsubscribeFunc = () => void;
@@ -123,6 +123,7 @@ export declare class RangeProgression extends Emitter<number> {
123
123
  * @returns Listenable: emits interpolated values
124
124
  */
125
125
  tween<T extends Tweenable>(from: T, to: T): Emitter<T>;
126
+ tween<T extends BlendableWith<T, R>, R>(from: T, to: R): Emitter<T>;
126
127
  /**
127
128
  * Creates a chainable progress emitter that quantises progress, as emitted by its parent, to the nearest of `steps` discrete values.
128
129
  *
@@ -182,7 +183,7 @@ export declare class RangeProgression extends Emitter<number> {
182
183
  *‎ 1
183
184
  *‎ | /
184
185
  *‎ o| /
185
- *‎ u|/ __ delta=.5
186
+ *‎ u|/ __ delta=.5
186
187
  *‎ t| /
187
188
  *‎ | /
188
189
  *‎ |___/__
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RangeProgression = exports.Emitter = void 0;
4
- const easing_1 = require("./easing");
5
- const tween_1 = require("./tween");
6
- const utils_1 = require("./utils");
7
- class Emitter {
1
+ import { easers } from "./easing";
2
+ import { tweenValue } from "./tween";
3
+ import { clamp } from "./utils";
4
+ export class Emitter {
8
5
  constructor(onListen) {
9
6
  this.onListen = onListen;
10
7
  /**
@@ -124,8 +121,7 @@ class Emitter {
124
121
  return this;
125
122
  }
126
123
  }
127
- exports.Emitter = Emitter;
128
- class RangeProgression extends Emitter {
124
+ export class RangeProgression extends Emitter {
129
125
  constructor() {
130
126
  super(...arguments);
131
127
  this.redirect = (listen) => new RangeProgression(listen);
@@ -134,14 +130,14 @@ class RangeProgression extends Emitter {
134
130
  if (!easer)
135
131
  return this;
136
132
  const easerFunc = typeof easer == "string"
137
- ? easing_1.easers[easer]
133
+ ? easers[easer]
138
134
  : easer;
139
135
  return new RangeProgression(easer ? (handler => this.onListen((progress) => {
140
136
  handler(easerFunc(progress));
141
137
  })) : h => this.onListen(h));
142
138
  }
143
139
  tween(from, to) {
144
- return new Emitter(handler => this.onListen(progress => handler((0, tween_1.tweenValue)(from, to, progress))));
140
+ return new Emitter(handler => this.onListen(progress => handler(tweenValue(from, to, progress))));
145
141
  }
146
142
  /**
147
143
  * Creates a chainable progress emitter that quantises progress, as emitted by its parent, to the nearest of `steps` discrete values.
@@ -156,7 +152,7 @@ class RangeProgression extends Emitter {
156
152
  }
157
153
  return new RangeProgression(handler => this.onListen(progress => {
158
154
  const snapped = Math.round(progress * steps) / steps;
159
- handler((0, utils_1.clamp)(snapped, 0, 1));
155
+ handler(clamp(snapped, 0, 1));
160
156
  }));
161
157
  }
162
158
  /**
@@ -177,7 +173,7 @@ class RangeProgression extends Emitter {
177
173
  * @returns Listenable: emits clamped progression values
178
174
  */
179
175
  clamp(min = 0, max = 1) {
180
- return new RangeProgression(handler => this.onListen(progress => handler((0, utils_1.clamp)(progress, min, max))));
176
+ return new RangeProgression(handler => this.onListen(progress => handler(clamp(progress, min, max))));
181
177
  }
182
178
  /**
183
179
  * Creates a chainable progress emitter that maps incoming values to a repeating linear scale
@@ -237,7 +233,7 @@ class RangeProgression extends Emitter {
237
233
  *‎ 1
238
234
  *‎ | /
239
235
  *‎ o| /
240
- *‎ u|/ __ delta=.5
236
+ *‎ u|/ __ delta=.5
241
237
  *‎ t| /
242
238
  *‎ | /
243
239
  *‎ |___/__
@@ -251,4 +247,3 @@ class RangeProgression extends Emitter {
251
247
  return new RangeProgression(handler => this.onListen(value => handler((value + delta) % 1)));
252
248
  }
253
249
  }
254
- exports.RangeProgression = RangeProgression;
package/internal/point.js CHANGED
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimelinePoint = void 0;
4
- const emitters_1 = require("./emitters");
5
- class TimelinePoint extends emitters_1.Emitter {
1
+ import { Emitter } from "./emitters";
2
+ export class TimelinePoint extends Emitter {
6
3
  /** @internal Manual construction of TimelinePoint is outside of the API contract and subject to undocumented change */
7
4
  constructor(onListen, timeline,
8
5
  /**
@@ -42,4 +39,3 @@ class TimelinePoint extends emitters_1.Emitter {
42
39
  return this.timeline.point(this.position + timeOffset);
43
40
  }
44
41
  }
45
- exports.TimelinePoint = TimelinePoint;
package/internal/range.js CHANGED
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimelineRange = void 0;
4
- const emitters_1 = require("./emitters");
5
- const point_1 = require("./point");
6
- const utils_1 = require("./utils");
7
- class TimelineRange extends emitters_1.RangeProgression {
1
+ import { RangeProgression } from "./emitters";
2
+ import { TimelinePoint } from "./point";
3
+ import { clamp } from "./utils";
4
+ export class TimelineRange extends RangeProgression {
8
5
  /** @internal Manual construction of RangeProgression is outside of the API contract and subject to undocumented change */
9
6
  constructor(onListen, timeline, startPosition,
10
7
  /** The duration of this range */
@@ -15,8 +12,8 @@ class TimelineRange extends emitters_1.RangeProgression {
15
12
  this.duration = duration;
16
13
  this.redirect = (listen) => new TimelineRange(listen, this.timeline, this.startPosition, this.duration);
17
14
  this.start = timeline.point(startPosition);
18
- this.end = timeline.point(startPosition + duration);
19
15
  this.endPosition = startPosition + duration;
16
+ this.end = timeline.point(this.endPosition);
20
17
  }
21
18
  /**
22
19
  * Creates two ranges by seperating one at a given point
@@ -58,7 +55,7 @@ class TimelineRange extends emitters_1.RangeProgression {
58
55
  * @returns Listenable: this range will emit a progression value (0..1) when a `seek()` passes or intersects it
59
56
  */
60
57
  grow(delta, anchor = 0) {
61
- const clampedAnchor = (0, utils_1.clamp)(anchor, 0, 1);
58
+ const clampedAnchor = clamp(anchor, 0, 1);
62
59
  const leftDelta = -delta * (1 - clampedAnchor);
63
60
  const rightDelta = delta * clampedAnchor;
64
61
  const newStart = this.startPosition + leftDelta;
@@ -79,7 +76,7 @@ class TimelineRange extends emitters_1.RangeProgression {
79
76
  if (factor <= 0) {
80
77
  throw new RangeError('scale factor must be > 0');
81
78
  }
82
- const clampedAnchor = (0, utils_1.clamp)(anchor, 0, 1);
79
+ const clampedAnchor = clamp(anchor, 0, 1);
83
80
  const oldLen = this.endPosition - this.startPosition;
84
81
  const pivot = this.startPosition + oldLen * clampedAnchor;
85
82
  const newStart = pivot - (pivot - this.startPosition) * factor;
@@ -91,10 +88,9 @@ class TimelineRange extends emitters_1.RangeProgression {
91
88
  return this.timeline.range(newStart, newEnd - newStart);
92
89
  }
93
90
  contains(target) {
94
- const [targetStart, targetEnd] = target instanceof point_1.TimelinePoint
91
+ const [targetStart, targetEnd] = target instanceof TimelinePoint
95
92
  ? [target.position, target.position]
96
93
  : [target.startPosition, target.startPosition + target.duration];
97
94
  return targetStart >= this.startPosition && targetEnd < this.endPosition;
98
95
  }
99
96
  }
100
- exports.TimelineRange = TimelineRange;
@@ -1,10 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Timeline = void 0;
4
- exports.animate = animate;
5
- const point_1 = require("./point");
6
- const range_1 = require("./range");
7
- const utils_1 = require("./utils");
1
+ import { TimelinePoint } from "./point";
2
+ import { TimelineRange } from "./range";
3
+ import { clamp } from "./utils";
8
4
  const default_fps = 60;
9
5
  const EndAction = {
10
6
  pause: 0,
@@ -17,10 +13,10 @@ const EndAction = {
17
13
  * @param duration
18
14
  * @returns Object representing a range on a single-use, autoplaying Timeline
19
15
  */
20
- function animate(duration) {
16
+ export function animate(duration) {
21
17
  return new Timeline(true).range(0, duration);
22
18
  }
23
- class Timeline {
19
+ export class Timeline {
24
20
  get currentTime() { return this._currentTime; }
25
21
  set currentTime(v) {
26
22
  this.seek(v);
@@ -109,7 +105,7 @@ class Timeline {
109
105
  }
110
106
  };
111
107
  };
112
- return new point_1.TimelinePoint(addHandler, this, position);
108
+ return new TimelinePoint(addHandler, this, position);
113
109
  }
114
110
  range(start = 0, optionalDuration) {
115
111
  const startPoint = typeof start == "number"
@@ -145,7 +141,7 @@ class Timeline {
145
141
  }
146
142
  };
147
143
  };
148
- return new range_1.TimelineRange(addHandler, this, startPosition, duration);
144
+ return new TimelineRange(addHandler, this, startPosition, duration);
149
145
  }
150
146
  getWrappedPosition(n) {
151
147
  if (this.endAction.type !== EndAction.wrap)
@@ -239,7 +235,7 @@ class Timeline {
239
235
  // filter ranges that overlap seeked range
240
236
  if (Math.min(position, end) <= Math.max(to, fromTime)
241
237
  && Math.min(to, fromTime) <= Math.max(position, end)) {
242
- let progress = (0, utils_1.clamp)((to - range.position) / range.duration, 0, 1);
238
+ let progress = clamp((to - range.position) / range.duration, 0, 1);
243
239
  range.handlers.slice().forEach(h => h(progress));
244
240
  }
245
241
  });
@@ -344,7 +340,6 @@ class Timeline {
344
340
  return this._currentTime;
345
341
  }
346
342
  }
347
- exports.Timeline = Timeline;
348
343
  const sortEvents = (a, b) => {
349
344
  return a.position - b.position;
350
345
  };
@@ -4,5 +4,8 @@ export type Tweenable = number | number[] | string | Blendable;
4
4
  export interface Blendable {
5
5
  blend(target: this, progress: number): this;
6
6
  }
7
+ export interface BlendableWith<T, R> {
8
+ blend(target: R, progress: number): T;
9
+ }
7
10
  /** @internal */
8
11
  export declare function tweenValue<T extends Tweenable>(from: T, to: T, progress: number): T;
package/internal/tween.js CHANGED
@@ -1,9 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tweenValue = tweenValue;
4
- const utils_1 = require("./utils");
1
+ import { clamp } from "./utils";
5
2
  /** @internal */
6
- function tweenValue(from, to, progress) {
3
+ export function tweenValue(from, to, progress) {
7
4
  if (Array.isArray(from)) {
8
5
  const toArr = to;
9
6
  if (from.length != toArr.length)
@@ -89,7 +86,7 @@ function parseColour(code) {
89
86
  function blendColours(from, to, bias) {
90
87
  const fromColour = parseColour(from);
91
88
  const toColour = parseColour(to);
92
- const blended = fromColour.map((val, i) => (0, utils_1.clamp)(blendNumbers(val, toColour[i], bias), 0, 255));
89
+ const blended = fromColour.map((val, i) => clamp(blendNumbers(val, toColour[i], bias), 0, 255));
93
90
  return ("#" + blended.map(n => Math.round(n).toString(16).padStart(2, "0")).join("")).replace(/ff$/, "");
94
91
  }
95
92
  const tweenableTokenRegex = /(#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b|[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/g;
package/internal/utils.js CHANGED
@@ -1,6 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.clamp = void 0;
4
1
  /** @internal */
5
- const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
6
- exports.clamp = clamp;
2
+ export const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/timeline",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/timeline",
6
6
  "type": "github"