@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.
- package/index.d.ts +219 -0
- package/index.js +316 -0
- 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