@xtia/timeline 0.2.4

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.
Files changed (3) hide show
  1. package/index.d.ts +219 -0
  2. package/index.js +316 -0
  3. package/package.json +14 -0
package/index.d.ts ADDED
@@ -0,0 +1,219 @@
1
+ type ApplyTweenFunc = (v: number) => void;
2
+ type EaseFunc = (v: number) => number;
3
+ interface TweenData {
4
+ time: number;
5
+ duration: number;
6
+ startValue: NumberOrNumberFunction;
7
+ endValue: NumberOrNumberFunction;
8
+ apply: ApplyTweenFunc;
9
+ ease?: EaseFunc;
10
+ }
11
+ type NumberOrNumberFunction = number | (() => number);
12
+ type EndAction = "continue" | "pause" | "loop";
13
+ interface TimelineChain {
14
+ /**
15
+ * Add a callback to run at the end of the previous item in a chain, offset by the Timeline's current position
16
+ * @param time Offset, time at which this event will be triggered
17
+ * @param apply Called when a forward seek targets or passes this event's time.
18
+ * @param revert Called when a backward seek passes this event's time. Default is no action
19
+ */
20
+ then(apply: () => void, revert?: () => void): TimelineChain;
21
+ /**
22
+ * Add a callback to run at the end of the previous item in a chain, offset by the Timeline's current position
23
+ * @param time Offset, time at which this event will be triggered
24
+ * @param apply Called when a forward seek targets or passes this event's time.
25
+ * @param applyOnReverse Pass true to call `apply` regardless of direction of traversal, or false to specify no reversion action
26
+ */
27
+ then(apply: () => void, applyOnReverse: boolean): TimelineChain;
28
+ /**
29
+ * Add a tween to this Timeline, starting at the end of the previous item in a chain
30
+ * @param duration Duration of tween
31
+ * @param apply Function called per frame, passed a value between `from` and `to` (with possible overshoot depending on easer)
32
+ * @param from Initial (pre-start) value to pass to `apply()`. If passed a function it will be called for each frame calculation. (default 0)
33
+ * @param to Final (post-end) value to pass to `apply()`. If passed a function it will be called for each frame calculation. (default 1)
34
+ * @param ease Function that takes progression (generally between 0 and 1) and returns modified progression
35
+ */
36
+ thenTween(duration: number, apply: ApplyTweenFunc, from?: NumberOrNumberFunction, to?: NumberOrNumberFunction, ease?: EaseFunc): TimelineChain;
37
+ /**
38
+ * Ensures end time extends to (at least) the specified time past the end of the previous item in a chain
39
+ * @param duration
40
+ */
41
+ thenWait(duration: number): TimelineChain;
42
+ }
43
+ export declare class Timeline {
44
+ private tweens;
45
+ private events;
46
+ private _position;
47
+ private _end;
48
+ private seeking;
49
+ private playInterval;
50
+ private currentSortDirection;
51
+ private smoothSeekTimeline?;
52
+ pauseAtEnd: boolean;
53
+ /**
54
+ * @property Sets a time at which to rewind back to 0. If `true`, looping will occur after the final tween or event.
55
+ */
56
+ loop: number | boolean;
57
+ /**
58
+ * @property Time delta multiplier to change speed. Affects `step()` and `play()`
59
+ */
60
+ timeScale: number;
61
+ /**
62
+ * @param autoPlay Pass true to autoplay at 60 FPS
63
+ * @param atEnd Choose what happens when the Timeline seeks beyond its final frame. Default is "pause"
64
+ */
65
+ constructor(autoPlay?: boolean, atEnd?: EndAction);
66
+ /**
67
+ * @param fps Pass a number, as frames per second, to autoplay at the specified frame rate.
68
+ * @param atEnd Choose what happens when the Timeline seeks beyond its final frame. Default is "pause"
69
+ */
70
+ constructor(fps?: number | boolean, atEnd?: EndAction);
71
+ get position(): number;
72
+ /**
73
+ * @property The final position at which an event or tween exists on this Timeline
74
+ */
75
+ get end(): number;
76
+ /**
77
+ * Add a tween to this Timeline
78
+ * @param startTime Position at which tween starts
79
+ * @param duration Duration of tween
80
+ * @param apply Function called per frame, passed a value between `from` and `to` (with possible overshoot depending on easer)
81
+ * @param from Initial (pre-start) value to pass to `apply()`. If passed a function it will be called for each frame calculation. (default 0)
82
+ * @param to Final (post-end) value to pass to `apply()`. If passed a function it will be called for each frame calculation. (default 1)
83
+ * @param ease Function that takes progression (generally between 0 and 1) and returns modified progression
84
+ * @returns A chaining interface for the time at which the tween ends
85
+ */
86
+ tween(startTime: number, duration: number, apply: ApplyTweenFunc, from?: NumberOrNumberFunction, to?: NumberOrNumberFunction, ease?: EaseFunc): TimelineChain;
87
+ /**
88
+ * Add a tween to this Timeline
89
+ * @param tween Object describing the tween
90
+ * @returns A chaining interface for the time at which the tween ends
91
+ */
92
+ tween(tween: TweenData): TimelineChain;
93
+ /**
94
+ * Add a callback to run at a specified time
95
+ * @param time Time at which this event will be triggered
96
+ * @param apply Called when a forward seek targets or passes this event's time
97
+ * @param applyOnReverse Called when a backward seek passes this event's time. Default is no action
98
+ * @returns A chaining interface for the specified time
99
+ */
100
+ at(time: number, apply: () => void, applyOnReverse?: () => void): TimelineChain;
101
+ /**
102
+ * Add a callback to run at a specified time
103
+ * @param time Time at which this event will be triggered
104
+ * @param apply Called when a forward seek targets or passes this event's time
105
+ * @param applyOnReverse Pass true to call `apply` regardless of direction of traversal, or false to specify no reversion action
106
+ * @returns A chaining interface for the specified time
107
+ */
108
+ at(time: number, apply: () => void, applyOnReverse: boolean): TimelineChain;
109
+ /**
110
+ * Ensures end time extends to (at least) the specified time
111
+ * @param time
112
+ * @returns A chaining interface for the specified time
113
+ */
114
+ at(time: number): TimelineChain;
115
+ /**
116
+ * Add a callback to run at a specified time
117
+ * @param time Time at which this event will be triggered
118
+ * @param apply Called when a forward seek targets or passes this event's time
119
+ * @param applyOnReverse Called when a backward seek targets or passes this event's time
120
+ * @returns A chaining interface for the specified time
121
+ */
122
+ at(time: number, apply: false, applyOnReverse: () => void): TimelineChain;
123
+ /**
124
+ * Add a callback to run at a specified time, offset by the Timeline's current position
125
+ * @param time Offset, time at which this event will be triggered
126
+ * @param apply Called when a forward seek targets or passes this event's time
127
+ * @param revert Called when a backward seek passes this event's time. Default is no action
128
+ * @returns A chaining interface for the specified time
129
+ */
130
+ in(time: number, apply?: () => void, revert?: () => void): TimelineChain;
131
+ /**
132
+ * Add a callback to run at a specified time, offset by the Timeline's current position (plus, if playing, time since the last update)
133
+ * @param time Offset, time at which this event will be triggered
134
+ * @param apply Called when a forward seek targets or passes this event's time
135
+ * @param applyOnReverse Pass true to call `apply` regardless of direction of traversal, or false to specify no reversion action
136
+ * @returns A chaining interface for the specified time
137
+ */
138
+ in(time: number, apply: () => void, applyOnReverse: boolean): TimelineChain;
139
+ /**
140
+ * Set the Timeline's position, applying intersecting tweens and events
141
+ * Ignores the `loop` property and sets position as specified
142
+ * @param time The new position
143
+ * @param smooth Milliseconds over which to smoothly seek
144
+ */
145
+ seek(time: number, smooth: number): void;
146
+ /**
147
+ * Set the Timeline's position, applying intersecting tweens and events
148
+ * Ignores the `loop` property and sets position as specified
149
+ * @param time The new position
150
+ * @param smooth Pass true to smooth-seek over 400ms
151
+ */
152
+ seek(time: number, smooth?: boolean): void;
153
+ private _seek;
154
+ /**
155
+ * Moves the timeline forwards (or backwards given negative delta) by a specified amount, applying intersecting tweens and events
156
+ * Honours `loop` ending action
157
+ * @param delta Amount of time to move forward by. This will be affected by the `timeScale` property
158
+ */
159
+ step(delta?: number, smooth?: number | boolean): void;
160
+ private lastFrameTime;
161
+ /**
162
+ * Begins moving forward in time from current `position` at a rate of 1000 x `timeScale` units per second
163
+ * If the timeline is already playing, it will stop and resume at the specified `fps`
164
+ * Honours `loop` and `pause` ending actions
165
+ * @param fps Frames per second
166
+ */
167
+ play(fps?: number): void;
168
+ /**
169
+ * Pauses automatic progression started via `play()`
170
+ */
171
+ pause(): void;
172
+ get isPlaying(): boolean;
173
+ /**
174
+ * Creates a Timeline as a tween
175
+ * * Transposes the inner Timeline's `0` -> `innerDuration` to the parent Timeline's `startTime` -> `outerDuration`
176
+ * * Experimental - this functionality may change in future versions
177
+ * @param startTime Position on the parent Timeline at which the inner Timeline will start seeking
178
+ * @param outerDuration Length of time occupied on the parent Timeline by the inner Timeline
179
+ * @param innerDuration Duration of the inner Timeline to transpose to `startTime` -> `outerDuration`
180
+ * @param ease Function that takes progression (generally between 0 and 1) and returns modified progression
181
+ * @returns
182
+ */
183
+ createInnerTimeline(startTime: number, outerDuration: number, innerDuration?: number, ease?: EaseFunc): Timeline;
184
+ private createChainInterface;
185
+ private seekTweens;
186
+ /**
187
+ * Initialises and starts a Timeline using chained calls to specify tweens and events
188
+ * @param looping
189
+ * @returns
190
+ */
191
+ static start(looping?: boolean): TimelineChain;
192
+ }
193
+ export declare const easers: {
194
+ linear: (x: number) => number;
195
+ easeIn: (x: number) => number;
196
+ easeIn4: (x: number) => number;
197
+ easeOut: (x: number) => number;
198
+ easeOut4: (x: number) => number;
199
+ circleIn: (x: number) => number;
200
+ circleIn4: (x: number) => number;
201
+ circleOut: (x: number) => number;
202
+ circleOut4: (x: number) => number;
203
+ easeInOut: (x: number) => number;
204
+ elastic: (x: number) => number;
205
+ overshootIn: (x: number) => number;
206
+ bounce: (x: number) => number;
207
+ noise: (x: number) => number;
208
+ step2: (x: number) => 0 | 1 | 0.5;
209
+ step3: (x: number) => 0 | 1 | 0.333 | 0.667;
210
+ pingpong: (x: number) => number;
211
+ };
212
+ export declare const dynamicEasers: {
213
+ average: (easers: EaseFunc[]) => (x: number) => number;
214
+ blend: (fromEaser: EaseFunc, toEaser: EaseFunc, amount: number) => (x: number) => number;
215
+ sine: (freq?: number) => (x: number) => number;
216
+ multiply: (easer: EaseFunc, mul: number) => (x: number) => number;
217
+ combine: (easers: EaseFunc[]) => (x: number) => number;
218
+ };
219
+ export {};
package/index.js ADDED
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ // PRE-PRODUCTION / ALPHA 0.2.4
3
+ // Copyright, 2024, Aleta Lovelace
4
+ // Licensed under CC BY-SA 4.0 <http://creativecommons.org/licenses/by-sa/4.0/>
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.dynamicEasers = exports.easers = exports.Timeline = void 0;
7
+ const blendNumbers = (from, to, progress) => from + progress * (to - from);
8
+ const applyTween = (tween, position) => {
9
+ let progress = (position - tween.time) / tween.duration;
10
+ const startValue = Number(typeof tween.startValue == "function" ? tween.startValue() : tween.startValue);
11
+ const endValue = Number(typeof tween.endValue == "function" ? tween.endValue() : tween.endValue);
12
+ if (progress <= 0) {
13
+ tween.apply(startValue);
14
+ }
15
+ else if (progress >= 1) {
16
+ tween.apply(endValue);
17
+ }
18
+ else {
19
+ if (tween.ease)
20
+ progress = tween.ease(progress);
21
+ tween.apply(blendNumbers(startValue, endValue, progress));
22
+ }
23
+ };
24
+ const sortEvents = (a, b) => {
25
+ return a.time - b.time;
26
+ };
27
+ const sortTweens = (a, b) => {
28
+ return (a.time + a.duration) - (b.time + b.duration);
29
+ };
30
+ const sortReverse = (a, b) => {
31
+ if (a.time == b.time)
32
+ return 1;
33
+ return b.time - a.time;
34
+ };
35
+ const copyObject = (source, properties) => Object.fromEntries(properties.map(p => [p, source[p]]));
36
+ class Timeline {
37
+ tweens = [];
38
+ events = [];
39
+ _position = 0;
40
+ _end = 0;
41
+ seeking = false;
42
+ playInterval;
43
+ currentSortDirection = 0;
44
+ smoothSeekTimeline;
45
+ pauseAtEnd;
46
+ /**
47
+ * @property Sets a time at which to rewind back to 0. If `true`, looping will occur after the final tween or event.
48
+ */
49
+ loop = false;
50
+ /**
51
+ * @property Time delta multiplier to change speed. Affects `step()` and `play()`
52
+ */
53
+ timeScale = 1;
54
+ constructor(autoPlay = false, atEnd = "pause") {
55
+ if (atEnd == "pause") {
56
+ this.pauseAtEnd = true;
57
+ }
58
+ else if (atEnd == "loop") {
59
+ this.loop = true;
60
+ }
61
+ if (autoPlay !== false)
62
+ this.play(autoPlay === true ? 60 : autoPlay);
63
+ }
64
+ get position() { return this._position; }
65
+ /**
66
+ * @property The final position at which an event or tween exists on this Timeline
67
+ */
68
+ get end() { return this._end; }
69
+ tween(startOrTween, duration, apply, from = 0, to = 1, ease) {
70
+ const tweenRecord = typeof startOrTween == "object" ? copyObject(startOrTween, ["apply", "duration", "ease", "endValue", "time", "startValue"]) : {
71
+ time: startOrTween,
72
+ duration,
73
+ startValue: from,
74
+ endValue: to,
75
+ apply,
76
+ ease,
77
+ };
78
+ this.tweens.push(tweenRecord);
79
+ const endTime = tweenRecord.time + tweenRecord.duration;
80
+ if (endTime > this._end)
81
+ this._end = endTime;
82
+ this.currentSortDirection = 0;
83
+ this.seek(this.position);
84
+ return this.createChainInterface(endTime);
85
+ }
86
+ at(time, apply = false, revert = false) {
87
+ if (time > this._end)
88
+ this._end = time;
89
+ if (apply || revert) {
90
+ this.events.push({ apply, revert: revert === true ? apply : revert, time });
91
+ this.currentSortDirection = 0;
92
+ if (time <= this._position && apply)
93
+ apply();
94
+ }
95
+ return this.createChainInterface(time);
96
+ }
97
+ in(time, apply, revert = false) {
98
+ // todo, subtract lastFrameTime difference if playing? [/ done?]
99
+ // todo, test this
100
+ if (this.isPlaying)
101
+ time -= (new Date().getTime() - this.lastFrameTime) * this.timeScale;
102
+ return this.at(time + this._position, apply, revert);
103
+ }
104
+ seek(time, smooth = false) {
105
+ if (this.seeking)
106
+ throw new Error("Seeking is not allowed within timeline events");
107
+ const interrupting = this.smoothSeekTimeline;
108
+ if (interrupting) {
109
+ interrupting.pause();
110
+ this.smoothSeekTimeline = undefined;
111
+ }
112
+ if (smooth === false)
113
+ return this._seek(time);
114
+ if (smooth === true)
115
+ smooth = 400;
116
+ this.smoothSeekTimeline = new Timeline(true);
117
+ this.smoothSeekTimeline.tween(0, smooth, v => this._seek(v), this._position, time, interrupting ? exports.easers.easeOut : exports.easers.easeInOut);
118
+ }
119
+ _seek(time) {
120
+ if (time === this._position)
121
+ return;
122
+ const fromPosition = this._position;
123
+ const reversing = time < fromPosition;
124
+ const requiredSortDirection = reversing ? -1 : 1;
125
+ if (this.currentSortDirection != requiredSortDirection) {
126
+ this.events.sort(reversing ? sortReverse : sortEvents);
127
+ this.tweens.sort(reversing ? sortReverse : sortTweens);
128
+ this.currentSortDirection = requiredSortDirection;
129
+ }
130
+ this.events.forEach(ev => {
131
+ const eventOverlaps = reversing
132
+ ? (ev.time <= fromPosition) && (ev.time > time)
133
+ : (ev.time > fromPosition) && (ev.time <= time);
134
+ if (!eventOverlaps)
135
+ return;
136
+ // timeline.position and tween states reflect the event's exact position during its handler
137
+ this.seekTweens(ev.time);
138
+ this._position = ev.time;
139
+ if (reversing) {
140
+ if (ev.revert)
141
+ ev.revert(); // ev.revert?.() produced a 'false not callable' error :/
142
+ }
143
+ else {
144
+ if (ev.apply)
145
+ ev.apply();
146
+ }
147
+ });
148
+ this.seekTweens(time);
149
+ this._position = time;
150
+ this.seeking = false;
151
+ }
152
+ /**
153
+ * Moves the timeline forwards (or backwards given negative delta) by a specified amount, applying intersecting tweens and events
154
+ * Honours `loop` ending action
155
+ * @param delta Amount of time to move forward by. This will be affected by the `timeScale` property
156
+ */
157
+ step(delta = 1, smooth = false) {
158
+ this.lastFrameTime = new Date().getTime();
159
+ const newPosition = this._position + delta * this.timeScale;
160
+ const loopAt = this.loop === true ? this._end : this.loop;
161
+ if (loopAt === false || newPosition < loopAt) {
162
+ this.seek(newPosition, smooth);
163
+ return;
164
+ }
165
+ const loopOvershoot = newPosition - loopAt;
166
+ this.seek(loopAt);
167
+ this.seek(0);
168
+ this.step(loopOvershoot);
169
+ }
170
+ lastFrameTime = new Date().getTime();
171
+ /**
172
+ * Begins moving forward in time from current `position` at a rate of 1000 x `timeScale` units per second
173
+ * If the timeline is already playing, it will stop and resume at the specified `fps`
174
+ * Honours `loop` and `pause` ending actions
175
+ * @param fps Frames per second
176
+ */
177
+ play(fps = 60) {
178
+ if (this.playInterval !== null)
179
+ this.pause();
180
+ this.lastFrameTime = new Date().getTime();
181
+ this.playInterval = setInterval(() => {
182
+ this.step(new Date().getTime() - this.lastFrameTime);
183
+ if (this.pauseAtEnd && this._position >= this._end) {
184
+ this.pause();
185
+ this._position = this._end;
186
+ }
187
+ }, 1000 / fps);
188
+ }
189
+ /**
190
+ * Pauses automatic progression started via `play()`
191
+ */
192
+ pause() {
193
+ if (this.playInterval !== null) {
194
+ clearInterval(this.playInterval);
195
+ this.playInterval = null;
196
+ }
197
+ }
198
+ get isPlaying() {
199
+ return this.playInterval !== null;
200
+ }
201
+ /**
202
+ * Creates a Timeline as a tween
203
+ * * Transposes the inner Timeline's `0` -> `innerDuration` to the parent Timeline's `startTime` -> `outerDuration`
204
+ * * Experimental - this functionality may change in future versions
205
+ * @param startTime Position on the parent Timeline at which the inner Timeline will start seeking
206
+ * @param outerDuration Length of time occupied on the parent Timeline by the inner Timeline
207
+ * @param innerDuration Duration of the inner Timeline to transpose to `startTime` -> `outerDuration`
208
+ * @param ease Function that takes progression (generally between 0 and 1) and returns modified progression
209
+ * @returns
210
+ */
211
+ createInnerTimeline(startTime, outerDuration, innerDuration = 1, ease) {
212
+ const innerTimeline = new Timeline(false);
213
+ this.tween(startTime, outerDuration, (v) => innerTimeline.seek(v), 0, innerDuration, ease);
214
+ return innerTimeline;
215
+ }
216
+ createChainInterface(timeOffset) {
217
+ return {
218
+ then: (apply, revert) => {
219
+ return this.at(timeOffset, apply, revert);
220
+ },
221
+ thenTween: (duration, apply, from = 0, to = 0, ease) => {
222
+ return this.tween(timeOffset, duration, apply, from, to, ease);
223
+ },
224
+ thenWait: (duration) => {
225
+ return this.at(timeOffset + duration);
226
+ },
227
+ };
228
+ }
229
+ seekTweens(toTime) {
230
+ const fromTime = this._position;
231
+ this.tweens.forEach((tween) => {
232
+ const { duration, time: start } = tween;
233
+ const end = start + duration;
234
+ // filter tweens that overlap seeked range
235
+ if (Math.min(start, end) <= Math.max(toTime, fromTime)
236
+ && Math.min(toTime, fromTime) <= Math.max(start, end)) {
237
+ applyTween(tween, toTime);
238
+ }
239
+ });
240
+ }
241
+ /**
242
+ * Initialises and starts a Timeline using chained calls to specify tweens and events
243
+ * @param looping
244
+ * @returns
245
+ */
246
+ static start(looping = false) {
247
+ const tl = new Timeline(true, looping ? "loop" : "pause");
248
+ tl.loop = looping;
249
+ return tl.at(0);
250
+ }
251
+ }
252
+ exports.Timeline = Timeline;
253
+ const overshoot = 1.70158;
254
+ exports.easers = {
255
+ linear: (x) => x,
256
+ easeIn: (x) => x * x,
257
+ easeIn4: (x) => Math.pow(x, 4),
258
+ easeOut: (x) => 1 - Math.pow((1 - x), 2),
259
+ easeOut4: (x) => 1 - Math.pow((1 - x), 4),
260
+ circleIn: (x) => 1 - Math.sqrt(1 - Math.pow(x, 2)),
261
+ circleIn4: (x) => 1 - Math.sqrt(1 - Math.pow(x, 4)),
262
+ circleOut: (x) => Math.sqrt(1 - Math.pow((1 - x), 2)),
263
+ circleOut4: (x) => Math.sqrt(1 - Math.pow((1 - x), 4)),
264
+ easeInOut: (x) => -Math.cos(x * Math.PI) / 2 + .5,
265
+ elastic: (x) => 1 - Math.cos(4 * Math.PI * x) * (1 - x),
266
+ overshootIn: (x) => --x * x * ((overshoot + 1) * x + overshoot) + 1,
267
+ bounce: (x) => {
268
+ if (x < 4 / 11.0) {
269
+ return (121 * x * x) / 16.0;
270
+ }
271
+ else if (x < 8 / 11.0) {
272
+ return (363 / 40.0 * x * x) - (99 / 10.0 * x) + 17 / 5.0;
273
+ }
274
+ else if (x < 9 / 10.0) {
275
+ return (4356 / 361.0 * x * x) - (35442 / 1805.0 * x) + 16061 / 1805.0;
276
+ }
277
+ else {
278
+ return (54 / 5.0 * x * x) - (513 / 25.0 * x) + 268 / 25.0;
279
+ }
280
+ },
281
+ noise: (x) => x == 0 ? 0 : (x >= 1 ? 1 : Math.random()),
282
+ step2: (x) => {
283
+ if (x < 0.333)
284
+ return 0;
285
+ if (x < 0.667)
286
+ return 0.5;
287
+ return 1;
288
+ },
289
+ step3: (x) => {
290
+ if (x < .25)
291
+ return 0;
292
+ if (x < .5)
293
+ return .333;
294
+ if (x < .75)
295
+ return .667;
296
+ return 1;
297
+ },
298
+ pingpong: (x) => x < .5 ? x * 2 : 1 - (x - .5) * 2,
299
+ };
300
+ exports.dynamicEasers = {
301
+ average: (easers) => (x) => {
302
+ return easers.reduce((n, easer) => n + easer(x), 0) / easers.length;
303
+ },
304
+ blend: (fromEaser, toEaser, amount) => (x) => {
305
+ return blendNumbers(fromEaser(x), toEaser(x), amount);
306
+ },
307
+ sine: (freq = 2) => (x) => Math.sin(x * freq * Math.PI * 2),
308
+ multiply: (easer, mul) => (x) => {
309
+ for (let i = 0; i < mul; i++)
310
+ x = easer(x);
311
+ return x;
312
+ },
313
+ combine: (easers) => (x) => {
314
+ return easers.reduce((n, easer) => easer(n), x);
315
+ },
316
+ };
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@xtia/timeline",
3
+ "version": "0.2.4",
4
+ "description": "Temporal state coordination",
5
+ "main": "index.ts",
6
+ "keywords": [
7
+ "timeline",
8
+ "animation",
9
+ "tweening"
10
+ ],
11
+ "author": "Aleta Lovelace",
12
+ "license": "MIT"
13
+ }
14
+