physics-animator 0.1.0 → 0.2.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/dist/cjs/AnimationSequencer.js +156 -0
- package/dist/cjs/Animator.js +310 -0
- package/{src/Spring.ts → dist/cjs/Spring.js} +38 -80
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/react/index.js +19 -0
- package/dist/cjs/react/useAnimator.js +29 -0
- package/dist/cjs/react/useSpringState.js +16 -0
- package/dist/cjs/react/useSpringValue.js +59 -0
- package/dist/cjs/three/ThreeAnimator.js +453 -0
- package/dist/cjs/three/index.js +17 -0
- package/dist/esm/package.json +1 -0
- package/package.json +17 -10
- package/src/AnimationSequencer.ts +0 -193
- package/src/Animator.ts +0 -377
- package/src/index.ts +0 -3
- package/src/react/index.ts +0 -3
- package/src/react/useAnimator.ts +0 -45
- package/src/react/useSpringState.ts +0 -22
- package/src/react/useSpringValue.ts +0 -81
- package/src/three/ThreeAnimator.ts +0 -605
- package/src/three/index.ts +0 -1
- package/tsconfig.json +0 -14
- /package/dist/{AnimationSequencer.js → esm/AnimationSequencer.js} +0 -0
- /package/dist/{Animator.js → esm/Animator.js} +0 -0
- /package/dist/{Spring.js → esm/Spring.js} +0 -0
- /package/dist/{index.js → esm/index.js} +0 -0
- /package/dist/{react → esm/react}/index.js +0 -0
- /package/dist/{react → esm/react}/useAnimator.js +0 -0
- /package/dist/{react → esm/react}/useSpringState.js +0 -0
- /package/dist/{react → esm/react}/useSpringValue.js +0 -0
- /package/dist/{three → esm/three}/ThreeAnimator.js +0 -0
- /package/dist/{three → esm/three}/index.js +0 -0
- /package/dist/{AnimationSequencer.d.ts → types/AnimationSequencer.d.ts} +0 -0
- /package/dist/{Animator.d.ts → types/Animator.d.ts} +0 -0
- /package/dist/{Spring.d.ts → types/Spring.d.ts} +0 -0
- /package/dist/{index.d.ts → types/index.d.ts} +0 -0
- /package/dist/{react → types/react}/index.d.ts +0 -0
- /package/dist/{react → types/react}/useAnimator.d.ts +0 -0
- /package/dist/{react → types/react}/useSpringState.d.ts +0 -0
- /package/dist/{react → types/react}/useSpringValue.d.ts +0 -0
- /package/dist/{three → types/three}/ThreeAnimator.d.ts +0 -0
- /package/dist/{three → types/three}/index.d.ts +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AnimationSequencer = void 0;
|
|
4
|
+
const event_signal_1 = require("@haxiomic/event-signal");
|
|
5
|
+
class AnimationSequencer {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.timeoutHandles = [];
|
|
8
|
+
this.intervalHandles = [];
|
|
9
|
+
this.sequences = [];
|
|
10
|
+
/**
|
|
11
|
+
* Execute a serial sequence of steps, firing a callback at each step. We either wait for the onCompleteEvent to be fired, or we wait for maxWait milliseconds before moving on to the next step.
|
|
12
|
+
*
|
|
13
|
+
* @returns Sequence - a function that can be called to stop the sequence
|
|
14
|
+
*/
|
|
15
|
+
this.runSequence = (steps) => {
|
|
16
|
+
let sequenceEvents = {
|
|
17
|
+
onStep: new event_signal_1.EventSignal(),
|
|
18
|
+
onComplete: new event_signal_1.EventSignal(),
|
|
19
|
+
onFinally: new event_signal_1.EventSignal(),
|
|
20
|
+
onError: new event_signal_1.EventSignal(),
|
|
21
|
+
};
|
|
22
|
+
let openListeners = new Set();
|
|
23
|
+
let timeoutHandles = new Array();
|
|
24
|
+
let stepIndex = 0;
|
|
25
|
+
const executeStep = (index) => {
|
|
26
|
+
try {
|
|
27
|
+
// check if we've reached the end of the sequence
|
|
28
|
+
if (index >= steps.length) {
|
|
29
|
+
sequenceEvents.onComplete.dispatch();
|
|
30
|
+
sequenceEvents.onFinally.dispatch({
|
|
31
|
+
complete: true,
|
|
32
|
+
stepIndex: steps.length - 1,
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let step = steps[index];
|
|
37
|
+
if (!step) {
|
|
38
|
+
throw new Error(`Step at index ${index} is undefined`);
|
|
39
|
+
}
|
|
40
|
+
sequenceEvents.onStep.dispatch(index);
|
|
41
|
+
let timeoutHandle = null;
|
|
42
|
+
let completeListener = null;
|
|
43
|
+
if (step.onCompleteEvent) {
|
|
44
|
+
let listener = step.onCompleteEvent.once(() => {
|
|
45
|
+
openListeners.delete(listener);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (step.maxWait_ms) {
|
|
49
|
+
timeoutHandle = this.setTimeout(() => next(), step.maxWait_ms);
|
|
50
|
+
timeoutHandles.push(timeoutHandle);
|
|
51
|
+
}
|
|
52
|
+
if (!step.onCompleteEvent && !step.maxWait_ms) {
|
|
53
|
+
next();
|
|
54
|
+
}
|
|
55
|
+
let hasFinished = false;
|
|
56
|
+
function next() {
|
|
57
|
+
if (hasFinished)
|
|
58
|
+
return;
|
|
59
|
+
clearTimeout(timeoutHandle);
|
|
60
|
+
completeListener?.remove();
|
|
61
|
+
stepIndex++;
|
|
62
|
+
hasFinished = true;
|
|
63
|
+
executeStep(stepIndex);
|
|
64
|
+
}
|
|
65
|
+
let result = step.callback();
|
|
66
|
+
if (result['then']) {
|
|
67
|
+
result.then(() => {
|
|
68
|
+
// if no onCompleteEvent, then we can move on to the next step
|
|
69
|
+
if (!step.onCompleteEvent) {
|
|
70
|
+
next();
|
|
71
|
+
}
|
|
72
|
+
}).catch((error) => {
|
|
73
|
+
sequenceEvents.onError.dispatch(error);
|
|
74
|
+
stop();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
sequenceEvents.onError.dispatch(error);
|
|
80
|
+
stop();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
let stopped = false;
|
|
84
|
+
function stop() {
|
|
85
|
+
if (stopped)
|
|
86
|
+
return;
|
|
87
|
+
for (let listener of openListeners) {
|
|
88
|
+
listener.remove();
|
|
89
|
+
}
|
|
90
|
+
for (let handle of timeoutHandles) {
|
|
91
|
+
clearTimeout(handle);
|
|
92
|
+
}
|
|
93
|
+
sequenceEvents.onFinally.dispatch({
|
|
94
|
+
complete: false,
|
|
95
|
+
stepIndex,
|
|
96
|
+
});
|
|
97
|
+
stopped = true;
|
|
98
|
+
}
|
|
99
|
+
// promise interface
|
|
100
|
+
let promise = new Promise((resolve, reject) => {
|
|
101
|
+
sequenceEvents.onComplete.once(() => resolve());
|
|
102
|
+
sequenceEvents.onFinally.once(() => resolve());
|
|
103
|
+
sequenceEvents.onError.once((error) => reject(error));
|
|
104
|
+
});
|
|
105
|
+
let sequence = {
|
|
106
|
+
stop,
|
|
107
|
+
events: sequenceEvents,
|
|
108
|
+
promise,
|
|
109
|
+
};
|
|
110
|
+
// track sequence
|
|
111
|
+
this.sequences.push(sequence);
|
|
112
|
+
sequenceEvents.onFinally.once(() => {
|
|
113
|
+
let index = this.sequences.indexOf(sequence);
|
|
114
|
+
this.sequences.splice(index, 1);
|
|
115
|
+
});
|
|
116
|
+
// start
|
|
117
|
+
executeStep(stepIndex);
|
|
118
|
+
return sequence;
|
|
119
|
+
};
|
|
120
|
+
this.registerSequence = (sequence) => {
|
|
121
|
+
this.sequences.push(sequence);
|
|
122
|
+
sequence.events.onFinally.once(() => {
|
|
123
|
+
let index = this.sequences.indexOf(sequence);
|
|
124
|
+
this.sequences.splice(index, 1);
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
this.setTimeout = (callback, delay) => {
|
|
128
|
+
let handle = window.setTimeout(callback, delay);
|
|
129
|
+
this.timeoutHandles.push(handle);
|
|
130
|
+
return handle;
|
|
131
|
+
};
|
|
132
|
+
this.setInterval = (callback, delay) => {
|
|
133
|
+
let handle = window.setInterval(callback, delay);
|
|
134
|
+
this.intervalHandles.push(handle);
|
|
135
|
+
return handle;
|
|
136
|
+
};
|
|
137
|
+
this.stopAllTimeouts = () => {
|
|
138
|
+
this.timeoutHandles.forEach(handle => clearTimeout(handle));
|
|
139
|
+
this.timeoutHandles = [];
|
|
140
|
+
};
|
|
141
|
+
this.stopAllIntervals = () => {
|
|
142
|
+
this.intervalHandles.forEach(handle => clearInterval(handle));
|
|
143
|
+
this.intervalHandles = [];
|
|
144
|
+
};
|
|
145
|
+
this.stopAllSequences = () => {
|
|
146
|
+
this.sequences.forEach(sequence => sequence.stop());
|
|
147
|
+
this.sequences = [];
|
|
148
|
+
};
|
|
149
|
+
this.stopAll = () => {
|
|
150
|
+
this.stopAllTimeouts();
|
|
151
|
+
this.stopAllIntervals();
|
|
152
|
+
this.stopAllSequences();
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.AnimationSequencer = AnimationSequencer;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Tween = exports.Animator = void 0;
|
|
4
|
+
const event_signal_1 = require("@haxiomic/event-signal");
|
|
5
|
+
const Spring_js_1 = require("./Spring.js");
|
|
6
|
+
var AnimationType;
|
|
7
|
+
(function (AnimationType) {
|
|
8
|
+
AnimationType[AnimationType["Spring"] = 0] = "Spring";
|
|
9
|
+
AnimationType[AnimationType["Tween"] = 1] = "Tween";
|
|
10
|
+
})(AnimationType || (AnimationType = {}));
|
|
11
|
+
/**
|
|
12
|
+
* Physically based animation of numeric properties of objects
|
|
13
|
+
*
|
|
14
|
+
* Designed to avoid discontinuities for smooth animation in all conditions
|
|
15
|
+
*/
|
|
16
|
+
class Animator {
|
|
17
|
+
constructor(onBeforeStep, onAfterStep) {
|
|
18
|
+
this.onBeforeStep = new event_signal_1.EventSignal();
|
|
19
|
+
this.onAfterStep = new event_signal_1.EventSignal();
|
|
20
|
+
this._onAnimationComplete = new event_signal_1.EventSignal();
|
|
21
|
+
this._onObjectAnimationsComplete = new event_signal_1.EventSignal();
|
|
22
|
+
this.animations = new Map();
|
|
23
|
+
this._springState = { x: 0, targetX: 0, v: 0 };
|
|
24
|
+
this.t_last = -1;
|
|
25
|
+
this._currentLoopControl = null;
|
|
26
|
+
if (onBeforeStep) {
|
|
27
|
+
this.onBeforeStep.addListener(e => onBeforeStep(e.dt_s));
|
|
28
|
+
}
|
|
29
|
+
if (onAfterStep) {
|
|
30
|
+
this.onAfterStep.addListener(e => onAfterStep(e.dt_s));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
springTo(object, field, target, params = { duration_s: 0.5 }) {
|
|
34
|
+
if (params != null) {
|
|
35
|
+
let spring = this.getAnimationOrCreate(object, field, AnimationType.Spring);
|
|
36
|
+
// update the target and parameters
|
|
37
|
+
spring.type = AnimationType.Spring;
|
|
38
|
+
spring.target = target;
|
|
39
|
+
spring.springParams = Spring_js_1.Spring.getPhysicsParameters(params);
|
|
40
|
+
spring.step = null;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.setTo(object, field, target);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
customTweenTo(object, field, target, duration_s, step) {
|
|
47
|
+
let animation = this.getAnimationOrCreate(object, field, AnimationType.Tween);
|
|
48
|
+
animation.type = AnimationType.Tween;
|
|
49
|
+
animation.target = target;
|
|
50
|
+
animation.tweenParams = {
|
|
51
|
+
x0: object[field],
|
|
52
|
+
t0_ms: performance.now(),
|
|
53
|
+
duration_s: duration_s,
|
|
54
|
+
};
|
|
55
|
+
animation.step = step;
|
|
56
|
+
}
|
|
57
|
+
linearTo(object, field, target, duration_s) {
|
|
58
|
+
this.customTweenTo(object, field, target, duration_s, Tween.linearStep);
|
|
59
|
+
}
|
|
60
|
+
easeInOutTo(object, field, target, duration_s) {
|
|
61
|
+
this.customTweenTo(object, field, target, duration_s, Tween.easeInOutStep);
|
|
62
|
+
}
|
|
63
|
+
easeInTo(object, field, target, duration_s) {
|
|
64
|
+
this.customTweenTo(object, field, target, duration_s, Tween.easeInStep);
|
|
65
|
+
}
|
|
66
|
+
easeOutTo(object, field, target, duration_s) {
|
|
67
|
+
this.customTweenTo(object, field, target, duration_s, Tween.easeOutStep);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Remove animation from the object and set the field to the target value
|
|
71
|
+
*/
|
|
72
|
+
setTo(object, field, target) {
|
|
73
|
+
this.remove(object, field);
|
|
74
|
+
object[field] = target;
|
|
75
|
+
}
|
|
76
|
+
onComplete(object, field, callback) {
|
|
77
|
+
return this._onAnimationComplete.addListener(e => {
|
|
78
|
+
if (e.object === object && e.field === field) {
|
|
79
|
+
callback(object, field);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
onAllComplete(object, callback, once) {
|
|
84
|
+
let listener = this._onObjectAnimationsComplete.addListener(e => {
|
|
85
|
+
if (e.object === object) {
|
|
86
|
+
callback(object);
|
|
87
|
+
if (once) {
|
|
88
|
+
listener.remove();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return listener;
|
|
93
|
+
}
|
|
94
|
+
step(dt_s) {
|
|
95
|
+
if (this.onBeforeStep.hasListeners()) {
|
|
96
|
+
this.onBeforeStep.dispatch({ dt_s });
|
|
97
|
+
}
|
|
98
|
+
let springState = this._springState;
|
|
99
|
+
// step all animations
|
|
100
|
+
this.animations.forEach((objectAnims, object) => {
|
|
101
|
+
objectAnims.forEach((animation, field) => {
|
|
102
|
+
switch (animation.type) {
|
|
103
|
+
case AnimationType.Spring:
|
|
104
|
+
{
|
|
105
|
+
// step the spring
|
|
106
|
+
springState.x = object[field];
|
|
107
|
+
springState.targetX = animation.target;
|
|
108
|
+
springState.v = animation.velocity;
|
|
109
|
+
if (animation.springParams != null) {
|
|
110
|
+
Spring_js_1.Spring.stepSpring(dt_s, springState, animation.springParams);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// instant transition: set to the target
|
|
114
|
+
springState.x = springState.targetX;
|
|
115
|
+
springState.v = 0;
|
|
116
|
+
}
|
|
117
|
+
// update the object
|
|
118
|
+
object[field] = springState.x;
|
|
119
|
+
animation.velocity = springState.v;
|
|
120
|
+
// remove the spring if it's close enough to the target and velocity is close to 0
|
|
121
|
+
if (Math.abs(springState.x - springState.targetX) < 0.0001 && Math.abs(springState.v) < 0.0001) {
|
|
122
|
+
object[field] = animation.target;
|
|
123
|
+
objectAnims.delete(field);
|
|
124
|
+
this._onAnimationComplete.dispatch({ object, field });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
case AnimationType.Tween: {
|
|
129
|
+
// step the tween
|
|
130
|
+
let x = object[field];
|
|
131
|
+
animation.step(object, field, animation.target, animation.tweenParams, dt_s);
|
|
132
|
+
let x_new = object[field];
|
|
133
|
+
animation.velocity = (x_new - x) / dt_s;
|
|
134
|
+
// remove the tween if it's complete
|
|
135
|
+
let deltaTime_s = (performance.now() - animation.tweenParams.t0_ms) / 1000;
|
|
136
|
+
if (deltaTime_s >= animation.tweenParams.duration_s) {
|
|
137
|
+
object[field] = animation.target;
|
|
138
|
+
objectAnims.delete(field);
|
|
139
|
+
this._onAnimationComplete.dispatch({ object, field });
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
// remove the object if it has no more springs
|
|
146
|
+
if (objectAnims.size == 0) {
|
|
147
|
+
this.animations.delete(object);
|
|
148
|
+
this._onObjectAnimationsComplete.dispatch({ object });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
this.onAfterStep.dispatch({ dt_s });
|
|
152
|
+
}
|
|
153
|
+
tick() {
|
|
154
|
+
let t_s = performance.now() / 1000;
|
|
155
|
+
let dt_s = this.t_last >= 0 ? t_s - this.t_last : 1 / 60;
|
|
156
|
+
this.t_last = t_s;
|
|
157
|
+
this.step(dt_s);
|
|
158
|
+
return dt_s;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Start the animation loop using requestAnimationFrame
|
|
162
|
+
*
|
|
163
|
+
* This will stop any existing animation loop
|
|
164
|
+
*/
|
|
165
|
+
startAnimationFrameLoop() {
|
|
166
|
+
this.stop();
|
|
167
|
+
let frameLoopHandle = -1;
|
|
168
|
+
let frameLoop = () => {
|
|
169
|
+
this.tick();
|
|
170
|
+
frameLoopHandle = window.requestAnimationFrame(frameLoop);
|
|
171
|
+
};
|
|
172
|
+
frameLoop();
|
|
173
|
+
this._currentLoopControl = {
|
|
174
|
+
stop: () => {
|
|
175
|
+
window.cancelAnimationFrame(frameLoopHandle);
|
|
176
|
+
},
|
|
177
|
+
start: () => {
|
|
178
|
+
frameLoop();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Start the animation loop using setTimeout
|
|
184
|
+
*
|
|
185
|
+
* This will stop any existing animation loop
|
|
186
|
+
*/
|
|
187
|
+
startIntervalLoop(interval_ms = 1000 / 240) {
|
|
188
|
+
this.stop();
|
|
189
|
+
let intervalHandle = -1;
|
|
190
|
+
let intervalLoop = () => {
|
|
191
|
+
this.tick();
|
|
192
|
+
intervalHandle = window.setTimeout(intervalLoop, interval_ms);
|
|
193
|
+
};
|
|
194
|
+
intervalLoop();
|
|
195
|
+
this._currentLoopControl = {
|
|
196
|
+
stop: () => {
|
|
197
|
+
window.clearTimeout(intervalHandle);
|
|
198
|
+
},
|
|
199
|
+
start: () => {
|
|
200
|
+
intervalLoop();
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
stop() {
|
|
205
|
+
if (this._currentLoopControl != null) {
|
|
206
|
+
this._currentLoopControl.stop();
|
|
207
|
+
this._currentLoopControl = null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Remove animation for this object and field if it exists
|
|
212
|
+
* Does not change the value of the field
|
|
213
|
+
*/
|
|
214
|
+
remove(object, field) {
|
|
215
|
+
let objectSprings = this.animations.get(object);
|
|
216
|
+
if (objectSprings != null) {
|
|
217
|
+
objectSprings.delete(field);
|
|
218
|
+
}
|
|
219
|
+
// if there are no more springs for this object, remove it from the map
|
|
220
|
+
if (objectSprings != null && objectSprings.size == 0) {
|
|
221
|
+
this.animations.delete(object);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Remove all animations for this object
|
|
226
|
+
*/
|
|
227
|
+
removeObject(object) {
|
|
228
|
+
this.animations.delete(object);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Remove all animations
|
|
232
|
+
*/
|
|
233
|
+
removeAll() {
|
|
234
|
+
this.animations.clear();
|
|
235
|
+
}
|
|
236
|
+
getVelocity(object, field) {
|
|
237
|
+
let spring = this.getObjectAnimations(object).get(field);
|
|
238
|
+
return spring?.velocity ?? 0;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Creates a new map if one doesn't already exist for the given object
|
|
242
|
+
*/
|
|
243
|
+
getObjectAnimations(object) {
|
|
244
|
+
let objectAnimations = this.animations.get(object);
|
|
245
|
+
if (objectAnimations == null) {
|
|
246
|
+
// create
|
|
247
|
+
objectAnimations = new Map();
|
|
248
|
+
this.animations.set(object, objectAnimations);
|
|
249
|
+
}
|
|
250
|
+
return objectAnimations;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Creates a new spring if one doesn't already exist for the given object and field
|
|
254
|
+
*/
|
|
255
|
+
getAnimationOrCreate(object, field, type) {
|
|
256
|
+
let objectAnimations = this.getObjectAnimations(object);
|
|
257
|
+
let animation = objectAnimations.get(field);
|
|
258
|
+
if (animation == null) {
|
|
259
|
+
// create
|
|
260
|
+
animation = {
|
|
261
|
+
target: 0,
|
|
262
|
+
type: type,
|
|
263
|
+
springParams: null,
|
|
264
|
+
tweenParams: null,
|
|
265
|
+
velocity: 0,
|
|
266
|
+
step: null
|
|
267
|
+
};
|
|
268
|
+
objectAnimations.set(field, animation);
|
|
269
|
+
}
|
|
270
|
+
animation.type = type;
|
|
271
|
+
return animation;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
exports.Animator = Animator;
|
|
275
|
+
var Tween;
|
|
276
|
+
(function (Tween) {
|
|
277
|
+
function linearStep(object, field, target, params, dt_s) {
|
|
278
|
+
let dx = target - params.x0;
|
|
279
|
+
let t = (performance.now() - params.t0_ms) / 1000;
|
|
280
|
+
let u = t / params.duration_s;
|
|
281
|
+
let x_new = params.x0 + dx * u;
|
|
282
|
+
object[field] = x_new;
|
|
283
|
+
}
|
|
284
|
+
Tween.linearStep = linearStep;
|
|
285
|
+
// cubic ease in out
|
|
286
|
+
function easeInOutStep(object, field, target, params, dt_s) {
|
|
287
|
+
let dx = target - params.x0;
|
|
288
|
+
let t = (performance.now() - params.t0_ms) / 1000;
|
|
289
|
+
let u = t / params.duration_s;
|
|
290
|
+
let x_new = params.x0 + dx * u * u * (3 - 2 * u);
|
|
291
|
+
object[field] = x_new;
|
|
292
|
+
}
|
|
293
|
+
Tween.easeInOutStep = easeInOutStep;
|
|
294
|
+
function easeInStep(object, field, target, params, dt_s) {
|
|
295
|
+
let dx = target - params.x0;
|
|
296
|
+
let t = (performance.now() - params.t0_ms) / 1000;
|
|
297
|
+
let u = t / params.duration_s;
|
|
298
|
+
let x_new = params.x0 + dx * u * u * u;
|
|
299
|
+
object[field] = x_new;
|
|
300
|
+
}
|
|
301
|
+
Tween.easeInStep = easeInStep;
|
|
302
|
+
function easeOutStep(object, field, target, params, dt_s) {
|
|
303
|
+
let dx = target - params.x0;
|
|
304
|
+
let t = (performance.now() - params.t0_ms) / 1000;
|
|
305
|
+
let u = t / params.duration_s;
|
|
306
|
+
let x_new = params.x0 + dx * (1 - Math.pow(1 - u, 3));
|
|
307
|
+
object[field] = x_new;
|
|
308
|
+
}
|
|
309
|
+
Tween.easeOutStep = easeOutStep;
|
|
310
|
+
})(Tween || (exports.Tween = Tween = {}));
|
|
@@ -1,51 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Spring
|
|
3
|
-
*
|
|
4
|
+
*
|
|
4
5
|
* @author George Corney (haxiomic)
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type UnderdampedParameters = {
|
|
13
|
-
/** Defined as the point in time we'll reach within 0.01% of target from 0 velocity start */
|
|
14
|
-
duration_s: number,
|
|
15
|
-
/**
|
|
16
|
-
* How soft / bouncy the spring, at 0 there is no bounce and decay is exponential, from 0 to infinity the spring will overshoot its target while decaying
|
|
17
|
-
* It can be loosely through of roughly the number of oscillations it will take to reach the target
|
|
18
|
-
*/
|
|
19
|
-
bounce: number,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type SpringParameters = ExponentialParameters | UnderdampedParameters | Spring.PhysicsParameters;
|
|
23
|
-
|
|
24
|
-
export namespace Spring {
|
|
25
|
-
|
|
26
|
-
export type PhysicsParameters = {
|
|
27
|
-
strength: number,
|
|
28
|
-
damping: number,
|
|
29
|
-
}
|
|
30
|
-
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.Spring = void 0;
|
|
9
|
+
var Spring;
|
|
10
|
+
(function (Spring) {
|
|
31
11
|
/**
|
|
32
12
|
* Starting with 0 velocity, this parameter describes how long it would take to reach half-way to the target
|
|
33
|
-
*
|
|
13
|
+
*
|
|
34
14
|
* `damping = 3.356694 / approxHalfLife_s`
|
|
35
|
-
*
|
|
15
|
+
*
|
|
36
16
|
* `strength = damping * damping / 4`
|
|
37
17
|
*/
|
|
38
|
-
|
|
18
|
+
function Exponential(options) {
|
|
39
19
|
// solved numerically
|
|
40
20
|
const halfLifeConstant = 3.356694; // from solve (1+u)*exp(-u)=0.5 for u, and constant = 2u
|
|
41
21
|
const pointOnePercentConstant = 18.46682; // from solve (1+u)*exp(-u)=0.001 for u, and constant = 2u
|
|
42
22
|
const damping = pointOnePercentConstant / options.duration_s;
|
|
43
|
-
|
|
44
23
|
let strength = damping * damping / 4;
|
|
45
24
|
return { damping, strength };
|
|
46
25
|
}
|
|
47
|
-
|
|
48
|
-
|
|
26
|
+
Spring.Exponential = Exponential;
|
|
27
|
+
function Underdamped(options) {
|
|
49
28
|
const { duration_s, bounce } = options;
|
|
50
29
|
// -2ln(0.001) = b t
|
|
51
30
|
const durationTarget = 0.001; // 0.1% of target
|
|
@@ -53,110 +32,89 @@ export namespace Spring {
|
|
|
53
32
|
// 4k - b^2 > 0
|
|
54
33
|
let bSq = damping * damping;
|
|
55
34
|
const criticalStrength = bSq / 4;
|
|
56
|
-
let strength = criticalStrength + (bounce * bounce + 1);
|
|
35
|
+
let strength = criticalStrength + (bounce * bounce + 1);
|
|
57
36
|
return { damping, strength };
|
|
58
37
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
parameters: SpringParameters
|
|
62
|
-
): PhysicsParameters {
|
|
38
|
+
Spring.Underdamped = Underdamped;
|
|
39
|
+
function getPhysicsParameters(parameters) {
|
|
63
40
|
if ('duration_s' in parameters) {
|
|
64
41
|
if ('bounce' in parameters) {
|
|
65
42
|
return Underdamped(parameters);
|
|
66
|
-
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
67
45
|
return Exponential(parameters);
|
|
68
46
|
}
|
|
69
|
-
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
70
49
|
// assume physics parameters
|
|
71
|
-
return parameters
|
|
50
|
+
return parameters;
|
|
72
51
|
}
|
|
73
52
|
}
|
|
74
|
-
|
|
53
|
+
Spring.getPhysicsParameters = getPhysicsParameters;
|
|
75
54
|
/**
|
|
76
55
|
* Analytic spring integration
|
|
77
|
-
* @param dt_s
|
|
78
|
-
* @param state
|
|
79
|
-
* @param parameters
|
|
56
|
+
* @param dt_s
|
|
57
|
+
* @param state
|
|
58
|
+
* @param parameters
|
|
80
59
|
*/
|
|
81
|
-
|
|
82
|
-
dt_s: number,
|
|
83
|
-
state: {
|
|
84
|
-
x: number,
|
|
85
|
-
targetX: number,
|
|
86
|
-
v: number,
|
|
87
|
-
},
|
|
88
|
-
parameters: PhysicsParameters
|
|
89
|
-
) {
|
|
60
|
+
function stepSpring(dt_s, state, parameters) {
|
|
90
61
|
// analytic integration (unconditionally stable)
|
|
91
62
|
// visualization: https://www.desmos.com/calculator/c2iug0kerh
|
|
92
63
|
// references:
|
|
93
64
|
// https://mathworld.wolfram.com/OverdampedSimpleHarmonicMotion.html
|
|
94
65
|
// https://mathworld.wolfram.com/CriticallyDampedSimpleHarmonicMotion.html
|
|
95
66
|
// https://mathworld.wolfram.com/UnderdampedSimpleHarmonicMotion.html
|
|
96
|
-
|
|
97
67
|
let k = parameters.strength;
|
|
98
68
|
let b = parameters.damping;
|
|
99
69
|
let t = dt_s;
|
|
100
70
|
let v0 = state.v;
|
|
101
71
|
let dx0 = state.x - state.targetX;
|
|
102
|
-
|
|
103
72
|
// nothing will change; exit early
|
|
104
|
-
if (dx0 === 0 && v0 === 0)
|
|
105
|
-
|
|
106
|
-
|
|
73
|
+
if (dx0 === 0 && v0 === 0)
|
|
74
|
+
return;
|
|
75
|
+
if (dt_s === 0)
|
|
76
|
+
return;
|
|
107
77
|
let critical = k * 4 - b * b;
|
|
108
|
-
|
|
109
78
|
if (critical > 0) {
|
|
110
79
|
// under damped
|
|
111
80
|
let q = 0.5 * Math.sqrt(critical); // γ
|
|
112
|
-
|
|
113
81
|
let A = dx0;
|
|
114
82
|
let B = ((b * dx0) * 0.5 + v0) / q;
|
|
115
|
-
|
|
116
83
|
let m = Math.exp(-b * 0.5 * t);
|
|
117
84
|
let c = Math.cos(q * t);
|
|
118
85
|
let s = Math.sin(q * t);
|
|
119
|
-
|
|
120
|
-
let
|
|
121
|
-
|
|
122
|
-
( B*q - 0.5*A*b) * c +
|
|
123
|
-
(-A*q - 0.5*b*B) * s
|
|
124
|
-
);
|
|
125
|
-
|
|
86
|
+
let dx1 = m * (A * c + B * s);
|
|
87
|
+
let v1 = m * ((B * q - 0.5 * A * b) * c +
|
|
88
|
+
(-A * q - 0.5 * b * B) * s);
|
|
126
89
|
state.v = v1;
|
|
127
90
|
state.x = dx1 + state.targetX;
|
|
128
|
-
}
|
|
91
|
+
}
|
|
92
|
+
else if (critical < 0) {
|
|
129
93
|
// over damped
|
|
130
94
|
let u = 0.5 * Math.sqrt(-critical);
|
|
131
95
|
let p = -0.5 * b + u;
|
|
132
96
|
let n = -0.5 * b - u;
|
|
133
|
-
let B = -(n*dx0 - v0)/(2*u);
|
|
97
|
+
let B = -(n * dx0 - v0) / (2 * u);
|
|
134
98
|
let A = dx0 - B;
|
|
135
|
-
|
|
136
99
|
let ep = Math.exp(p * t);
|
|
137
100
|
let en = Math.exp(n * t);
|
|
138
|
-
|
|
139
101
|
let dx1 = A * en + B * ep;
|
|
140
102
|
let v1 = A * n * en + B * p * ep;
|
|
141
|
-
|
|
142
103
|
state.v = v1;
|
|
143
104
|
state.x = dx1 + state.targetX;
|
|
144
|
-
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
145
107
|
// critically damped
|
|
146
108
|
let w = Math.sqrt(k); // ω
|
|
147
|
-
|
|
148
109
|
let A = dx0;
|
|
149
110
|
let B = v0 + w * dx0;
|
|
150
111
|
let e = Math.exp(-w * t);
|
|
151
|
-
|
|
152
112
|
let dx1 = (A + B * t) * e;
|
|
153
113
|
let v1 = (B - w * (A + B * t)) * e;
|
|
154
|
-
|
|
155
114
|
state.v = v1;
|
|
156
115
|
state.x = dx1 + state.targetX;
|
|
157
116
|
}
|
|
158
|
-
|
|
159
117
|
return 0.5 * k * state.x * state.x;
|
|
160
118
|
}
|
|
161
|
-
|
|
162
|
-
}
|
|
119
|
+
Spring.stepSpring = stepSpring;
|
|
120
|
+
})(Spring || (exports.Spring = Spring = {}));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./AnimationSequencer.js"), exports);
|
|
18
|
+
__exportStar(require("./Animator.js"), exports);
|
|
19
|
+
__exportStar(require("./Spring.js"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type": "commonjs"}
|