@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 +29 -29
- package/internal/emitters.d.ts +11 -5
- package/internal/emitters.js +15 -5
- package/internal/range.d.ts +16 -2
- package/internal/range.js +22 -3
- package/internal/timeline.js +5 -5
- package/package.json +1 -1
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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 *
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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.
|
|
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
|
-
.
|
|
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.
|
|
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
|
-
.
|
|
184
|
+
.apply(v => element.scrollTop = v);
|
|
185
185
|
|
|
186
186
|
// number arrays
|
|
187
187
|
range
|
|
188
188
|
.tween([0, 180], [360, 180])
|
|
189
|
-
.
|
|
189
|
+
.apply((angles) => pieChart.setValues(angles));
|
|
190
190
|
|
|
191
191
|
// strings
|
|
192
192
|
range
|
|
193
193
|
.tween("#000000", "#ff00ff")
|
|
194
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
653
|
+
.apply(s => document.title = s)
|
|
654
654
|
})
|
|
655
|
-
.
|
|
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
|
-
##### `
|
|
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
|
-
.
|
|
699
|
+
.apply(s => document.title = s)
|
|
700
700
|
})
|
|
701
|
-
.
|
|
701
|
+
.apply(v => progressBar.style.width = v);
|
|
702
702
|
```
|
|
703
703
|
|
|
704
704
|
|
package/internal/emitters.d.ts
CHANGED
|
@@ -16,11 +16,17 @@ export declare class Emitter<T> {
|
|
|
16
16
|
*/
|
|
17
17
|
protected redirect: (listen: ListenFunc<T>) => Emitter<T>;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
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
|
-
* .
|
|
74
|
+
* .apply(s => document.title = s)
|
|
69
75
|
* })
|
|
70
|
-
* .
|
|
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
|
-
* .
|
|
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
|
-
* .
|
|
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
|
package/internal/emitters.js
CHANGED
|
@@ -15,7 +15,7 @@ export class Emitter {
|
|
|
15
15
|
this.redirect = (listen) => new Emitter(listen);
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
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
|
-
* .
|
|
124
|
+
* .apply(s => document.title = s)
|
|
115
125
|
* })
|
|
116
|
-
* .
|
|
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
|
-
* .
|
|
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(
|
|
187
|
+
handler(snapped);
|
|
178
188
|
}));
|
|
179
189
|
}
|
|
180
190
|
/**
|
package/internal/range.d.ts
CHANGED
|
@@ -32,8 +32,22 @@ export declare class TimelineRange extends RangeProgression {
|
|
|
32
32
|
*/
|
|
33
33
|
spread(count: number): TimelinePoint[];
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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
|
-
*
|
|
48
|
-
*
|
|
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('
|
|
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;
|
package/internal/timeline.js
CHANGED
|
@@ -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(
|
|
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).
|
|
197
|
-
return new Promise(r => seeker.end.
|
|
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).
|
|
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.
|
|
332
|
+
point.apply(reverse
|
|
333
333
|
? (event => event.direction < 0 ? reverse() : action)
|
|
334
334
|
: action);
|
|
335
335
|
return this.createChainingInterface(point.position);
|