@freestylejs/ani-core 1.0.0 → 1.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/index.cjs +753 -383
- package/dist/index.d.cts +389 -263
- package/dist/index.d.ts +389 -263
- package/dist/index.js +745 -369
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -21,31 +21,60 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
AnimationClock: () => AnimationClock,
|
|
24
|
-
|
|
25
|
-
CompositionNode: () => CompositionNode,
|
|
24
|
+
BezierTimingFunction: () => BezierTimingFunction,
|
|
26
25
|
EventManager: () => EventManager,
|
|
27
26
|
LinearTimingFunction: () => LinearTimingFunction,
|
|
28
|
-
|
|
29
|
-
SegmentNode: () => SegmentNode,
|
|
30
|
-
SequenceNode: () => SequenceNode,
|
|
31
|
-
StaggerNode: () => StaggerNode,
|
|
27
|
+
RafAniTimeline: () => RafAniTimeline,
|
|
32
28
|
T: () => T,
|
|
33
|
-
|
|
29
|
+
TimelineBase: () => TimelineBase,
|
|
34
30
|
TimingFunction: () => TimingFunction,
|
|
31
|
+
WebAniTimeline: () => WebAniTimeline,
|
|
35
32
|
a: () => a,
|
|
36
|
-
|
|
33
|
+
calculateSegmentState: () => calculateSegmentState,
|
|
34
|
+
compileToKeyframes: () => compileToKeyframes,
|
|
37
35
|
createStates: () => createStates,
|
|
38
36
|
createStyleSheet: () => createStyleSheet,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
parallel: () => parallel,
|
|
42
|
-
sequence: () => sequence,
|
|
43
|
-
stagger: () => stagger,
|
|
44
|
-
timeline: () => timeline
|
|
37
|
+
rafTimeline: () => rafTimeline,
|
|
38
|
+
webTimeline: () => webTimeline
|
|
45
39
|
});
|
|
46
40
|
module.exports = __toCommonJS(src_exports);
|
|
47
41
|
|
|
48
|
-
// src/
|
|
42
|
+
// src/utils/time/is_end.ts
|
|
43
|
+
function isEndOfAnimation(currentT, duration, tolerance = 1e-3) {
|
|
44
|
+
return currentT === duration || currentT - duration >= tolerance;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/ani/core/engine.ts
|
|
48
|
+
function calculateSegmentState(localTime, segmentDef, dt = 0) {
|
|
49
|
+
const t = Math.max(0, Math.min(localTime, segmentDef.duration));
|
|
50
|
+
const animeValues = [];
|
|
51
|
+
let allComplete = true;
|
|
52
|
+
const isMultipleTiming = Array.isArray(segmentDef.timing);
|
|
53
|
+
if (isMultipleTiming && segmentDef.timing.length !== segmentDef.from.length) {
|
|
54
|
+
throw new TypeError(
|
|
55
|
+
`[calculateSegmentState] timing does not correctly set. It requires multiple timing for ${segmentDef.from}, but received ${segmentDef.timing}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < segmentDef.from.length; i++) {
|
|
59
|
+
const timingFunction = isMultipleTiming ? segmentDef.timing[i] : segmentDef.timing;
|
|
60
|
+
const animeResponse = timingFunction.step(t, {
|
|
61
|
+
dt,
|
|
62
|
+
from: segmentDef.from[i],
|
|
63
|
+
to: segmentDef.to[i],
|
|
64
|
+
duration: segmentDef.duration
|
|
65
|
+
});
|
|
66
|
+
animeValues.push(animeResponse.value);
|
|
67
|
+
if (!animeResponse.endOfAnimation) {
|
|
68
|
+
allComplete = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
values: animeValues,
|
|
73
|
+
isComplete: allComplete || isEndOfAnimation(t, segmentDef.duration)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/nodes/base.ts
|
|
49
78
|
var AnimationNode = class {
|
|
50
79
|
constructor(id) {
|
|
51
80
|
if (id) {
|
|
@@ -75,6 +104,113 @@ var TimingFunction = class _TimingFunction {
|
|
|
75
104
|
}
|
|
76
105
|
};
|
|
77
106
|
|
|
107
|
+
// src/timing/bezier.ts
|
|
108
|
+
var NEWTON_ITERATIONS = 4;
|
|
109
|
+
var NEWTON_MIN_SLOPE = 1e-3;
|
|
110
|
+
var SUBDIVISION_PRECISION = 1e-7;
|
|
111
|
+
var SUBDIVISION_MAX_ITERATIONS = 10;
|
|
112
|
+
var SAMPLE_TABLE_SIZE = 11;
|
|
113
|
+
var SAMPLE_STEP_SIZE = 1 / (SAMPLE_TABLE_SIZE - 1);
|
|
114
|
+
var BezierTimingFunction = class extends TimingFunction {
|
|
115
|
+
constructor(opt) {
|
|
116
|
+
super();
|
|
117
|
+
this.opt = opt;
|
|
118
|
+
this.sampleValues = null;
|
|
119
|
+
if (this.opt.p2.x !== this.opt.p2.y || this.opt.p3.x !== this.opt.p3.y) {
|
|
120
|
+
this.sampleValues = new Float32Array(SAMPLE_TABLE_SIZE);
|
|
121
|
+
for (let i = 0; i < SAMPLE_TABLE_SIZE; ++i) {
|
|
122
|
+
this.sampleValues[i] = this.calcBezier(
|
|
123
|
+
i * SAMPLE_STEP_SIZE,
|
|
124
|
+
this.opt.p2.x,
|
|
125
|
+
this.opt.p3.x
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
calcBezier(t, a1, a2) {
|
|
131
|
+
return ((1 - 3 * a2 + 3 * a1) * t + (3 * a2 - 6 * a1)) * t * t + 3 * a1 * t;
|
|
132
|
+
}
|
|
133
|
+
getSlope(t, a1, a2) {
|
|
134
|
+
return 3 * (1 - 3 * a2 + 3 * a1) * t * t + 2 * (3 * a2 - 6 * a1) * t + 3 * a1;
|
|
135
|
+
}
|
|
136
|
+
getTForX(x) {
|
|
137
|
+
const mX1 = this.opt.p2.x;
|
|
138
|
+
const mX2 = this.opt.p3.x;
|
|
139
|
+
let intervalStart = 0;
|
|
140
|
+
let currentSample = 1;
|
|
141
|
+
const lastSample = SAMPLE_TABLE_SIZE - 1;
|
|
142
|
+
for (; currentSample !== lastSample && this.sampleValues[currentSample] <= x; ++currentSample) {
|
|
143
|
+
intervalStart += SAMPLE_STEP_SIZE;
|
|
144
|
+
}
|
|
145
|
+
--currentSample;
|
|
146
|
+
const dist = (x - this.sampleValues[currentSample]) / (this.sampleValues[currentSample + 1] - this.sampleValues[currentSample]);
|
|
147
|
+
const guessForT = intervalStart + dist * SAMPLE_STEP_SIZE;
|
|
148
|
+
const initialSlope = this.getSlope(guessForT, mX1, mX2);
|
|
149
|
+
if (initialSlope >= NEWTON_MIN_SLOPE) {
|
|
150
|
+
return this.newtonRaphsonIterate(x, guessForT, mX1, mX2);
|
|
151
|
+
}
|
|
152
|
+
if (initialSlope === 0) {
|
|
153
|
+
return guessForT;
|
|
154
|
+
}
|
|
155
|
+
return this.binarySubdivide(
|
|
156
|
+
x,
|
|
157
|
+
intervalStart,
|
|
158
|
+
intervalStart + SAMPLE_STEP_SIZE,
|
|
159
|
+
mX1,
|
|
160
|
+
mX2
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
binarySubdivide(aX, aA, aB, mX1, mX2) {
|
|
164
|
+
let currentX;
|
|
165
|
+
let currentT;
|
|
166
|
+
let i = 0;
|
|
167
|
+
let currentA = aA;
|
|
168
|
+
let currentB = aB;
|
|
169
|
+
do {
|
|
170
|
+
currentT = currentA + (currentB - currentA) / 2;
|
|
171
|
+
currentX = this.calcBezier(currentT, mX1, mX2) - aX;
|
|
172
|
+
if (currentX > 0) {
|
|
173
|
+
currentB = currentT;
|
|
174
|
+
} else {
|
|
175
|
+
currentA = currentT;
|
|
176
|
+
}
|
|
177
|
+
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
|
|
178
|
+
return currentT;
|
|
179
|
+
}
|
|
180
|
+
newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
|
|
181
|
+
let guessT = aGuessT;
|
|
182
|
+
for (let i = 0; i < NEWTON_ITERATIONS; ++i) {
|
|
183
|
+
const currentSlope = this.getSlope(guessT, mX1, mX2);
|
|
184
|
+
if (currentSlope === 0) {
|
|
185
|
+
return guessT;
|
|
186
|
+
}
|
|
187
|
+
const currentX = this.calcBezier(guessT, mX1, mX2) - aX;
|
|
188
|
+
guessT -= currentX / currentSlope;
|
|
189
|
+
}
|
|
190
|
+
return guessT;
|
|
191
|
+
}
|
|
192
|
+
step(time, context) {
|
|
193
|
+
const { duration, from, to } = context;
|
|
194
|
+
if (duration === 0) {
|
|
195
|
+
return { value: to, endOfAnimation: true };
|
|
196
|
+
}
|
|
197
|
+
const x = Math.max(0, Math.min(time / duration, 1));
|
|
198
|
+
let easedT = x;
|
|
199
|
+
if (this.opt.p2.x !== this.opt.p2.y || this.opt.p3.x !== this.opt.p3.y) {
|
|
200
|
+
if (!this.sampleValues) {
|
|
201
|
+
}
|
|
202
|
+
const t = this.getTForX(x);
|
|
203
|
+
easedT = this.calcBezier(t, this.opt.p2.y, this.opt.p3.y);
|
|
204
|
+
}
|
|
205
|
+
const value = from + (to - from) * easedT;
|
|
206
|
+
const endOfAnimation = time >= duration;
|
|
207
|
+
return {
|
|
208
|
+
value,
|
|
209
|
+
endOfAnimation
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
78
214
|
// src/timing/linear.ts
|
|
79
215
|
var LinearTimingFunction = class extends TimingFunction {
|
|
80
216
|
step(time, context) {
|
|
@@ -84,29 +220,72 @@ var LinearTimingFunction = class extends TimingFunction {
|
|
|
84
220
|
}
|
|
85
221
|
};
|
|
86
222
|
|
|
87
|
-
// src/timing/
|
|
88
|
-
var
|
|
223
|
+
// src/timing/dynamic_spring.ts
|
|
224
|
+
var DynamicSpringTimingFunction = class extends TimingFunction {
|
|
89
225
|
constructor(opt) {
|
|
90
226
|
super();
|
|
91
227
|
this.opt = opt;
|
|
92
|
-
this.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
96
|
-
this.p4 = {
|
|
97
|
-
x: 1,
|
|
98
|
-
y: 1
|
|
99
|
-
};
|
|
228
|
+
this.currentValue = 0;
|
|
229
|
+
this.currentVelocity = 0;
|
|
230
|
+
this.isInitialized = false;
|
|
100
231
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
232
|
+
init(startValue) {
|
|
233
|
+
this.currentValue = startValue;
|
|
234
|
+
this.currentVelocity = 0;
|
|
235
|
+
this.isInitialized = true;
|
|
104
236
|
}
|
|
105
|
-
|
|
106
|
-
const
|
|
237
|
+
getDerivatives(state, to) {
|
|
238
|
+
const { m, k, c } = this.opt;
|
|
239
|
+
const displacement = state.x - to;
|
|
240
|
+
const a2 = (-k * displacement - c * state.v) / m;
|
|
241
|
+
return { dx: state.v, dv: a2 };
|
|
242
|
+
}
|
|
243
|
+
step(_time, context) {
|
|
244
|
+
if (!this.isInitialized) {
|
|
245
|
+
this.init(context.from);
|
|
246
|
+
}
|
|
247
|
+
const { to, tolerance, dt } = context;
|
|
248
|
+
if (dt === 0) {
|
|
249
|
+
return {
|
|
250
|
+
value: this.currentValue,
|
|
251
|
+
endOfAnimation: false
|
|
252
|
+
// Or check for end state
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const x = this.currentValue;
|
|
256
|
+
const v = this.currentVelocity;
|
|
257
|
+
const k1 = this.getDerivatives({ x, v }, to);
|
|
258
|
+
const k2State = {
|
|
259
|
+
x: x + k1.dx * (dt / 2),
|
|
260
|
+
v: v + k1.dv * (dt / 2)
|
|
261
|
+
};
|
|
262
|
+
const k2 = this.getDerivatives(k2State, to);
|
|
263
|
+
const k3State = {
|
|
264
|
+
x: x + k2.dx * (dt / 2),
|
|
265
|
+
v: v + k2.dv * (dt / 2)
|
|
266
|
+
};
|
|
267
|
+
const k3 = this.getDerivatives(k3State, to);
|
|
268
|
+
const k4State = {
|
|
269
|
+
x: x + k3.dx * dt,
|
|
270
|
+
v: v + k3.dv * dt
|
|
271
|
+
};
|
|
272
|
+
const k4 = this.getDerivatives(k4State, to);
|
|
273
|
+
const avgDx = 1 / 6 * (k1.dx + 2 * k2.dx + 2 * k3.dx + k4.dx);
|
|
274
|
+
const avgDv = 1 / 6 * (k1.dv + 2 * k2.dv + 2 * k3.dv + k4.dv);
|
|
275
|
+
this.currentValue = x + avgDx * dt;
|
|
276
|
+
this.currentVelocity = v + avgDv * dt;
|
|
277
|
+
const tol = tolerance ?? 1e-3;
|
|
278
|
+
const displacement = this.currentValue - to;
|
|
279
|
+
const isMoving = Math.abs(this.currentVelocity) > tol;
|
|
280
|
+
const isDisplaced = Math.abs(displacement) > tol;
|
|
281
|
+
const endOfAnimation = !isMoving && !isDisplaced;
|
|
282
|
+
if (endOfAnimation) {
|
|
283
|
+
this.currentValue = to;
|
|
284
|
+
this.currentVelocity = 0;
|
|
285
|
+
}
|
|
107
286
|
return {
|
|
108
|
-
value:
|
|
109
|
-
endOfAnimation
|
|
287
|
+
value: this.currentValue,
|
|
288
|
+
endOfAnimation
|
|
110
289
|
};
|
|
111
290
|
}
|
|
112
291
|
};
|
|
@@ -181,30 +360,62 @@ var T = {
|
|
|
181
360
|
* Creates a new Bezier timing function instance.
|
|
182
361
|
*
|
|
183
362
|
* @param {Bezier.BezierTimingFunctionOpt} opt - Options for configuring the Bezier curve.
|
|
184
|
-
* A new instance of BezierTimingFunction.
|
|
185
363
|
*/
|
|
186
364
|
bezier: (opt) => new BezierTimingFunction(opt),
|
|
187
365
|
/**
|
|
188
366
|
* Creates a new Spring timing function instance.
|
|
189
367
|
*
|
|
190
|
-
* @param {
|
|
191
|
-
* A new instance of SpringTimingFunction.
|
|
368
|
+
* @param {SpringTimingFunctionOpt} opt - Options for configuring the Spring timing function.
|
|
192
369
|
*/
|
|
193
370
|
spring: (opt) => new SpringTimingFunction(opt),
|
|
371
|
+
/**
|
|
372
|
+
* Creates a new Dynamic Spring timing function instance.
|
|
373
|
+
*
|
|
374
|
+
* @param SpringTimingFunctionOpt} opt - Options for configuring the Spring timing function.
|
|
375
|
+
*/
|
|
376
|
+
dynamicSpring: (opt) => new DynamicSpringTimingFunction(opt),
|
|
194
377
|
/**
|
|
195
378
|
* Creates linear timing function instance.
|
|
196
379
|
*/
|
|
197
|
-
linear: () => new LinearTimingFunction()
|
|
380
|
+
linear: () => new LinearTimingFunction(),
|
|
381
|
+
/**
|
|
382
|
+
* Standard CSS 'ease' timing function (0.25, 0.1, 0.25, 1.0).
|
|
383
|
+
*/
|
|
384
|
+
ease: () => new BezierTimingFunction({
|
|
385
|
+
p2: { x: 0.25, y: 0.1 },
|
|
386
|
+
p3: { x: 0.25, y: 1 }
|
|
387
|
+
}),
|
|
388
|
+
/**
|
|
389
|
+
* Standard CSS 'ease-in' timing function (0.42, 0, 1.0, 1.0).
|
|
390
|
+
*/
|
|
391
|
+
easeIn: () => new BezierTimingFunction({
|
|
392
|
+
p2: { x: 0.42, y: 0 },
|
|
393
|
+
p3: { x: 1, y: 1 }
|
|
394
|
+
}),
|
|
395
|
+
/**
|
|
396
|
+
* Standard CSS 'ease-out' timing function (0, 0, 0.58, 1.0).
|
|
397
|
+
*/
|
|
398
|
+
easeOut: () => new BezierTimingFunction({
|
|
399
|
+
p2: { x: 0, y: 0 },
|
|
400
|
+
p3: { x: 0.58, y: 1 }
|
|
401
|
+
}),
|
|
402
|
+
/**
|
|
403
|
+
* Standard CSS 'ease-in-out' timing function (0.42, 0, 0.58, 1.0).
|
|
404
|
+
*/
|
|
405
|
+
easeInOut: () => new BezierTimingFunction({
|
|
406
|
+
p2: { x: 0.42, y: 0 },
|
|
407
|
+
p3: { x: 0.58, y: 1 }
|
|
408
|
+
})
|
|
198
409
|
};
|
|
199
410
|
|
|
200
|
-
// src/
|
|
411
|
+
// src/nodes/segment.ts
|
|
201
412
|
var SegmentNode = class extends AnimationNode {
|
|
202
413
|
constructor(props, id) {
|
|
203
414
|
super(id);
|
|
204
415
|
this.type = "SEGMENT";
|
|
205
416
|
const nodeProps = {
|
|
206
417
|
to: props.to,
|
|
207
|
-
duration: props.duration
|
|
418
|
+
duration: props.duration,
|
|
208
419
|
...props.timing !== void 0 && { timing: props.timing }
|
|
209
420
|
};
|
|
210
421
|
this.props = nodeProps;
|
|
@@ -224,7 +435,7 @@ function ani(props, id) {
|
|
|
224
435
|
return new SegmentNode(props, id);
|
|
225
436
|
}
|
|
226
437
|
|
|
227
|
-
// src/
|
|
438
|
+
// src/nodes/composition.ts
|
|
228
439
|
var CompositionNode = class _CompositionNode extends AnimationNode {
|
|
229
440
|
constructor(children, timing, id) {
|
|
230
441
|
super(id);
|
|
@@ -253,12 +464,12 @@ var CompositionNode = class _CompositionNode extends AnimationNode {
|
|
|
253
464
|
}
|
|
254
465
|
};
|
|
255
466
|
|
|
256
|
-
// src/
|
|
467
|
+
// src/nodes/delay.ts
|
|
257
468
|
function delay(duration, id) {
|
|
258
469
|
return new SegmentNode({ to: {}, duration }, id);
|
|
259
470
|
}
|
|
260
471
|
|
|
261
|
-
// src/
|
|
472
|
+
// src/nodes/loop.ts
|
|
262
473
|
var LoopNode = class extends CompositionNode {
|
|
263
474
|
constructor(child, loopCount, timing, id) {
|
|
264
475
|
super([child], timing, id);
|
|
@@ -279,7 +490,7 @@ function loop(child, loopCount, timing, id) {
|
|
|
279
490
|
return new LoopNode(child, loopCount, timing, id);
|
|
280
491
|
}
|
|
281
492
|
|
|
282
|
-
// src/
|
|
493
|
+
// src/nodes/parallel.ts
|
|
283
494
|
var ParallelNode = class extends CompositionNode {
|
|
284
495
|
constructor(children, timing, id) {
|
|
285
496
|
const seenProperty = /* @__PURE__ */ new Set();
|
|
@@ -329,7 +540,7 @@ function parallel(children, timing, id) {
|
|
|
329
540
|
return new ParallelNode(children, timing, id);
|
|
330
541
|
}
|
|
331
542
|
|
|
332
|
-
// src/
|
|
543
|
+
// src/nodes/sequence.ts
|
|
333
544
|
var SequenceNode = class extends CompositionNode {
|
|
334
545
|
constructor(children, timing, id) {
|
|
335
546
|
super(children, timing, id);
|
|
@@ -348,12 +559,12 @@ function sequence(children, timing, id) {
|
|
|
348
559
|
return new SequenceNode(children, timing, id);
|
|
349
560
|
}
|
|
350
561
|
|
|
351
|
-
// src/
|
|
562
|
+
// src/nodes/stagger.ts
|
|
352
563
|
var StaggerNode = class extends CompositionNode {
|
|
353
|
-
constructor(children,
|
|
354
|
-
super(children,
|
|
564
|
+
constructor(children, offset, timing, id) {
|
|
565
|
+
super(children, timing, id);
|
|
355
566
|
this.type = "STAGGER";
|
|
356
|
-
this.offset =
|
|
567
|
+
this.offset = offset;
|
|
357
568
|
if (children.length === 0) {
|
|
358
569
|
this.duration = 0;
|
|
359
570
|
} else {
|
|
@@ -369,10 +580,81 @@ var StaggerNode = class extends CompositionNode {
|
|
|
369
580
|
}
|
|
370
581
|
}
|
|
371
582
|
};
|
|
372
|
-
function stagger(children,
|
|
373
|
-
return new StaggerNode(children,
|
|
583
|
+
function stagger(children, offset, timing, id) {
|
|
584
|
+
return new StaggerNode(children, offset, timing, id);
|
|
374
585
|
}
|
|
375
586
|
|
|
587
|
+
// src/ani/core/interface/timeline_interface.ts
|
|
588
|
+
var TimelineBase = class {
|
|
589
|
+
constructor(rootNode) {
|
|
590
|
+
this.rootNode = rootNode;
|
|
591
|
+
this._currentExecutionPlan = null;
|
|
592
|
+
this.duration = rootNode.duration;
|
|
593
|
+
this._baseExecutionPlan = this._constructExecutionPlan(rootNode);
|
|
594
|
+
this.play = this.play.bind(this);
|
|
595
|
+
this.pause = this.pause.bind(this);
|
|
596
|
+
this.seek = this.seek.bind(this);
|
|
597
|
+
this.reset = this.reset.bind(this);
|
|
598
|
+
this.resume = this.resume.bind(this);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* flatten the AST into a linear execution plan.
|
|
602
|
+
*/
|
|
603
|
+
_constructExecutionPlan(rootNode) {
|
|
604
|
+
const plan = [];
|
|
605
|
+
rootNode.construct(plan, 0);
|
|
606
|
+
return plan;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Merges the base plan with runtime dynamic overrides.
|
|
610
|
+
*/
|
|
611
|
+
_resolveExecutionPlan(keyframes, durations) {
|
|
612
|
+
if (!keyframes && !durations) {
|
|
613
|
+
return [...this._baseExecutionPlan];
|
|
614
|
+
}
|
|
615
|
+
const segmentNodes = this._baseExecutionPlan.filter(
|
|
616
|
+
(segment) => segment.node.type === "SEGMENT"
|
|
617
|
+
);
|
|
618
|
+
const segLength = segmentNodes.length;
|
|
619
|
+
if (keyframes && keyframes.length !== segLength) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
`[Timeline] Keyframe mismatch: Expected ${segLength}, received ${keyframes.length}.`
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (durations && durations.length !== segLength) {
|
|
625
|
+
throw new Error(
|
|
626
|
+
`[Timeline] Duration mismatch: Expected ${segLength}, received ${durations.length}.`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
const newPlan = [];
|
|
630
|
+
let keyframeIndex = 0;
|
|
631
|
+
for (const segment of this._baseExecutionPlan) {
|
|
632
|
+
if (segment.node.type === "SEGMENT") {
|
|
633
|
+
const dynamicTo = keyframes?.[keyframeIndex];
|
|
634
|
+
const dynamicDuration = durations?.[keyframeIndex];
|
|
635
|
+
const newSegmentProps = {
|
|
636
|
+
...segment.node.props,
|
|
637
|
+
...dynamicTo && dynamicTo !== "keep" && {
|
|
638
|
+
to: dynamicTo
|
|
639
|
+
},
|
|
640
|
+
...dynamicDuration && dynamicDuration !== "keep" && {
|
|
641
|
+
duration: dynamicDuration
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
const newSegment = new SegmentNode(
|
|
645
|
+
newSegmentProps,
|
|
646
|
+
segment.node.id
|
|
647
|
+
);
|
|
648
|
+
newPlan.push({ ...segment, node: newSegment });
|
|
649
|
+
keyframeIndex++;
|
|
650
|
+
} else {
|
|
651
|
+
newPlan.push({ ...segment });
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return newPlan;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
|
|
376
658
|
// src/loop/clock.ts
|
|
377
659
|
var AnimationClock = class _AnimationClock {
|
|
378
660
|
constructor(maxDeltaTime) {
|
|
@@ -428,234 +710,174 @@ var AnimationClock = class _AnimationClock {
|
|
|
428
710
|
}
|
|
429
711
|
};
|
|
430
712
|
|
|
431
|
-
// src/
|
|
432
|
-
function
|
|
433
|
-
|
|
713
|
+
// src/ani/core/resolver.ts
|
|
714
|
+
function resolveGroup(group) {
|
|
715
|
+
if (Array.isArray(group)) {
|
|
716
|
+
return { keyMap: null, values: group };
|
|
717
|
+
}
|
|
718
|
+
const typedGroup = group;
|
|
719
|
+
const keys = Object.keys(typedGroup);
|
|
720
|
+
const keyMap = new Map(keys.map((key, i) => [key, i]));
|
|
721
|
+
const values = keys.map((key) => typedGroup[key]);
|
|
722
|
+
return { keyMap, values };
|
|
434
723
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const t = Math.max(0, Math.min(localTime, segmentDef.duration));
|
|
439
|
-
const animeValues = [];
|
|
440
|
-
let allComplete = true;
|
|
441
|
-
const isMultipleTiming = Array.isArray(segmentDef.timing);
|
|
442
|
-
if (isMultipleTiming && segmentDef.timing.length !== segmentDef.from.length) {
|
|
443
|
-
throw new TypeError(
|
|
444
|
-
`[calculateSegmentState] timing does not correctly set. It requires multiple timing for ${segmentDef.from}, but received ${segmentDef.timing}`
|
|
445
|
-
);
|
|
724
|
+
function resolveStateToGroup(state, keyMap) {
|
|
725
|
+
if (!keyMap) {
|
|
726
|
+
return state;
|
|
446
727
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
728
|
+
const group = {};
|
|
729
|
+
for (const [key, index] of keyMap.entries()) {
|
|
730
|
+
group[key] = state[index];
|
|
731
|
+
}
|
|
732
|
+
return group;
|
|
733
|
+
}
|
|
734
|
+
function resolvePlanState(plan, initialValues, keyMap, targetTime, dt = 0) {
|
|
735
|
+
const nextState = [...initialValues];
|
|
736
|
+
let stateAtLastStartTime = [...initialValues];
|
|
737
|
+
for (const segment of plan) {
|
|
738
|
+
if (targetTime < segment.startTime) {
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
stateAtLastStartTime = [...nextState];
|
|
742
|
+
const { keyMap: segKeyMap, values: toValues } = resolveGroup(
|
|
743
|
+
segment.node.props.to
|
|
744
|
+
);
|
|
745
|
+
const isRecordAni = keyMap !== null;
|
|
746
|
+
let fromValues = [];
|
|
747
|
+
const timings = [];
|
|
748
|
+
const t = segment.node.props.timing;
|
|
749
|
+
const isRecordTiming = t && !(t instanceof TimingFunction);
|
|
750
|
+
if (isRecordAni) {
|
|
751
|
+
for (const key of segKeyMap.keys()) {
|
|
752
|
+
const index = keyMap.get(key);
|
|
753
|
+
fromValues.push(stateAtLastStartTime[index]);
|
|
754
|
+
if (isRecordTiming) {
|
|
755
|
+
timings.push(t[key]);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
} else {
|
|
759
|
+
fromValues = stateAtLastStartTime;
|
|
760
|
+
}
|
|
761
|
+
const localTime = targetTime - segment.startTime;
|
|
762
|
+
const segmentDef = {
|
|
763
|
+
from: fromValues,
|
|
764
|
+
to: toValues,
|
|
765
|
+
duration: segment.node.duration,
|
|
766
|
+
// default fallback = linear
|
|
767
|
+
timing: isRecordAni && isRecordTiming ? timings : t ?? T.linear()
|
|
768
|
+
};
|
|
769
|
+
const result = calculateSegmentState(localTime, segmentDef, dt);
|
|
770
|
+
const finalValues = result.isComplete ? toValues : result.values;
|
|
771
|
+
if (isRecordAni) {
|
|
772
|
+
let i = 0;
|
|
773
|
+
for (const key of segKeyMap.keys()) {
|
|
774
|
+
const stateIndex = keyMap.get(key);
|
|
775
|
+
if (stateIndex !== void 0 && stateIndex !== -1) {
|
|
776
|
+
nextState[stateIndex] = finalValues[i];
|
|
777
|
+
}
|
|
778
|
+
i++;
|
|
779
|
+
}
|
|
780
|
+
} else {
|
|
781
|
+
for (let i = 0; i < finalValues.length; i++) {
|
|
782
|
+
nextState[i] = finalValues[i];
|
|
783
|
+
}
|
|
458
784
|
}
|
|
459
785
|
}
|
|
460
|
-
return
|
|
461
|
-
values: animeValues,
|
|
462
|
-
isComplete: allComplete || isEndOfAnimation(t, segmentDef.duration)
|
|
463
|
-
};
|
|
786
|
+
return nextState;
|
|
464
787
|
}
|
|
465
788
|
|
|
466
|
-
// src/ani/timeline.ts
|
|
467
|
-
var
|
|
789
|
+
// src/ani/raf/timeline.ts
|
|
790
|
+
var RafAniTimeline = class extends TimelineBase {
|
|
468
791
|
constructor(rootNode, clock) {
|
|
469
|
-
|
|
470
|
-
this._currentExecutionPlan = null;
|
|
792
|
+
super(rootNode);
|
|
471
793
|
this._masterTime = 0;
|
|
794
|
+
this._delay = 0;
|
|
472
795
|
this._status = "IDLE";
|
|
473
796
|
this._currentConfig = null;
|
|
474
797
|
this._state = [];
|
|
475
798
|
this._initialState = [];
|
|
476
799
|
this._repeatCount = 0;
|
|
477
800
|
this._propertyKeyMap = null;
|
|
478
|
-
this._segmentStartStates = /* @__PURE__ */ new Map();
|
|
479
801
|
this._onUpdateCallbacks = /* @__PURE__ */ new Set();
|
|
480
|
-
this.duration = rootNode.duration;
|
|
481
|
-
this._baseExecutionPlan = this._constructExecutionPlan(rootNode);
|
|
482
802
|
this._clock = clock ?? AnimationClock.create();
|
|
483
|
-
this.play.bind(this);
|
|
484
|
-
this.pause.bind(this);
|
|
485
|
-
this.seek.bind(this);
|
|
486
|
-
this.resume.bind(this);
|
|
487
|
-
this.reset.bind(this);
|
|
803
|
+
this.play = this.play.bind(this);
|
|
804
|
+
this.pause = this.pause.bind(this);
|
|
805
|
+
this.seek = this.seek.bind(this);
|
|
806
|
+
this.resume = this.resume.bind(this);
|
|
807
|
+
this.reset = this.reset.bind(this);
|
|
488
808
|
}
|
|
489
|
-
/**
|
|
490
|
-
* Current animation running config.
|
|
491
|
-
*/
|
|
492
809
|
get currentConfig() {
|
|
493
810
|
return this._currentConfig;
|
|
494
811
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
const keyMap = new Map(Object.keys(group).map((key, i) => [key, i]));
|
|
503
|
-
const values = Object.values(group);
|
|
504
|
-
return { keyMap, values };
|
|
812
|
+
getCurrentValue() {
|
|
813
|
+
if (this._state.length === 0) return null;
|
|
814
|
+
return resolveStateToGroup(
|
|
815
|
+
this._state,
|
|
816
|
+
this._propertyKeyMap
|
|
817
|
+
);
|
|
505
818
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
_resolveStateToGroup(state) {
|
|
510
|
-
if (!this._propertyKeyMap) {
|
|
511
|
-
return state;
|
|
512
|
-
}
|
|
513
|
-
const group = {};
|
|
514
|
-
let i = 0;
|
|
515
|
-
for (const key of this._propertyKeyMap.keys()) {
|
|
516
|
-
group[key] = state[i];
|
|
517
|
-
i++;
|
|
819
|
+
_calculateStateAtTime(targetTime, dt = 0) {
|
|
820
|
+
if (this._initialState.length === 0 || !this._currentExecutionPlan) {
|
|
821
|
+
return [];
|
|
518
822
|
}
|
|
519
|
-
return
|
|
823
|
+
return resolvePlanState(
|
|
824
|
+
this._currentExecutionPlan,
|
|
825
|
+
this._initialState,
|
|
826
|
+
this._propertyKeyMap,
|
|
827
|
+
// Using the class property directly
|
|
828
|
+
targetTime,
|
|
829
|
+
dt
|
|
830
|
+
);
|
|
520
831
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
832
|
+
notify() {
|
|
833
|
+
for (const subscriber of this._onUpdateCallbacks) {
|
|
834
|
+
subscriber({
|
|
835
|
+
state: resolveStateToGroup(
|
|
836
|
+
this._state,
|
|
837
|
+
this._propertyKeyMap
|
|
838
|
+
),
|
|
839
|
+
status: this._status
|
|
840
|
+
});
|
|
841
|
+
}
|
|
528
842
|
}
|
|
529
843
|
/**
|
|
530
|
-
*
|
|
844
|
+
* @private Internal clock subscription callback.
|
|
531
845
|
*/
|
|
532
|
-
|
|
533
|
-
if (this.
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (targetTime < segment.startTime) {
|
|
540
|
-
continue;
|
|
541
|
-
}
|
|
542
|
-
if (!segment.node.props.timing) {
|
|
543
|
-
throw new Error(
|
|
544
|
-
`[Timeline] timing should be provided. Please specify timing using a.timing.(...). Check target segment: ${JSON.stringify(segment, null, 2)}.`,
|
|
545
|
-
{ cause: segment }
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
stateAtLastStartTime = [...nextState];
|
|
549
|
-
const { keyMap, values: toValues } = this._resolveGroup(
|
|
550
|
-
segment.node.props.to
|
|
551
|
-
);
|
|
552
|
-
const isRecordAni = this._propertyKeyMap !== null && keyMap !== null;
|
|
553
|
-
let fromValues = [];
|
|
554
|
-
if (isRecordAni) {
|
|
555
|
-
for (const key of keyMap.keys()) {
|
|
556
|
-
fromValues.push(
|
|
557
|
-
stateAtLastStartTime[this._propertyKeyMap.get(key)]
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
} else {
|
|
561
|
-
fromValues = stateAtLastStartTime;
|
|
562
|
-
}
|
|
563
|
-
let finalAnimeValues = [];
|
|
564
|
-
const localTime = targetTime - segment.startTime;
|
|
565
|
-
const segmentDef = {
|
|
566
|
-
from: fromValues,
|
|
567
|
-
to: toValues,
|
|
568
|
-
duration: segment.node.duration,
|
|
569
|
-
timing: segment.node.props.timing
|
|
570
|
-
};
|
|
571
|
-
const segmentState = calculateSegmentState(
|
|
572
|
-
localTime,
|
|
573
|
-
segmentDef,
|
|
574
|
-
dt
|
|
575
|
-
);
|
|
576
|
-
if (segmentState.isComplete) {
|
|
577
|
-
finalAnimeValues = toValues;
|
|
846
|
+
update(dt) {
|
|
847
|
+
if (this._status !== "PLAYING") return;
|
|
848
|
+
if (this._delay > 0) {
|
|
849
|
+
this._delay -= dt;
|
|
850
|
+
if (this._delay < 0) {
|
|
851
|
+
dt = -this._delay;
|
|
852
|
+
this._delay = 0;
|
|
578
853
|
} else {
|
|
579
|
-
|
|
854
|
+
return;
|
|
580
855
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
856
|
+
}
|
|
857
|
+
this._masterTime += dt;
|
|
858
|
+
if (this._masterTime >= this.duration) this._masterTime = this.duration;
|
|
859
|
+
this._state = this._calculateStateAtTime(this._masterTime, dt);
|
|
860
|
+
this.notify();
|
|
861
|
+
if (isEndOfAnimation(this._masterTime, this.duration)) {
|
|
862
|
+
this._repeatCount += 1;
|
|
863
|
+
const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
|
|
864
|
+
if (noRepeat) {
|
|
865
|
+
this._status = "ENDED";
|
|
866
|
+
this._clock.unsubscribe(this);
|
|
867
|
+
this.notify();
|
|
591
868
|
} else {
|
|
592
|
-
|
|
593
|
-
nextState[i] = finalAnimeValues[i];
|
|
594
|
-
}
|
|
869
|
+
this.play(this._currentConfig);
|
|
595
870
|
}
|
|
596
871
|
}
|
|
597
|
-
return nextState;
|
|
598
|
-
}
|
|
599
|
-
_resolveExecutionPlan(keyframes, durations) {
|
|
600
|
-
if (!keyframes && !durations) {
|
|
601
|
-
return [...this._baseExecutionPlan];
|
|
602
|
-
}
|
|
603
|
-
const segmentNodes = this._baseExecutionPlan.filter(
|
|
604
|
-
(segment) => segment.node.type === "SEGMENT"
|
|
605
|
-
);
|
|
606
|
-
const segLength = segmentNodes.length;
|
|
607
|
-
if (keyframes && keyframes.length !== segLength) {
|
|
608
|
-
throw new Error(
|
|
609
|
-
`Timeline keyframe mismatch: Expected ${segLength} keyframes, but received ${keyframes.length}.`
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
if (durations && durations.length !== segLength) {
|
|
613
|
-
throw new Error(
|
|
614
|
-
`Timeline keyframe mismatch: Expected ${segLength} durations, but received ${durations.length}.`
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
const newPlan = [];
|
|
618
|
-
let keyframeIndex = 0;
|
|
619
|
-
for (const segment of this._baseExecutionPlan) {
|
|
620
|
-
if (segment.node.type === "SEGMENT") {
|
|
621
|
-
const dynamicTo = keyframes?.[keyframeIndex];
|
|
622
|
-
const dynamicDuration = durations?.[keyframeIndex];
|
|
623
|
-
const newSegmentProps = {
|
|
624
|
-
...segment.node.props,
|
|
625
|
-
// >> dynamic to
|
|
626
|
-
...dynamicTo && {
|
|
627
|
-
to: dynamicTo === "keep" ? segment.node.props.to : dynamicTo
|
|
628
|
-
},
|
|
629
|
-
// >> dynamic duration
|
|
630
|
-
...dynamicDuration && {
|
|
631
|
-
duration: dynamicDuration === "keep" ? segment.node.props.duration : dynamicDuration
|
|
632
|
-
}
|
|
633
|
-
};
|
|
634
|
-
const newSegment = new SegmentNode(
|
|
635
|
-
newSegmentProps,
|
|
636
|
-
segment.node.id
|
|
637
|
-
);
|
|
638
|
-
newPlan.push({ ...segment, node: newSegment });
|
|
639
|
-
keyframeIndex++;
|
|
640
|
-
} else {
|
|
641
|
-
newPlan.push({ ...segment });
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
return newPlan;
|
|
645
|
-
}
|
|
646
|
-
notify() {
|
|
647
|
-
for (const subscriber of this._onUpdateCallbacks) {
|
|
648
|
-
subscriber({
|
|
649
|
-
state: this._resolveStateToGroup(this._state),
|
|
650
|
-
status: this._status
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
872
|
}
|
|
873
|
+
/**
|
|
874
|
+
* Plays animation.
|
|
875
|
+
* @param config {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame RequestAnimationFrame API} based config.
|
|
876
|
+
* @param canBeIntercepted if `true` animation can be intercepted even if already animation started.
|
|
877
|
+
*/
|
|
654
878
|
play(config, canBeIntercepted = true) {
|
|
655
|
-
if (this._status === "PLAYING" && !canBeIntercepted)
|
|
656
|
-
|
|
657
|
-
}
|
|
658
|
-
const isRepeating = this._currentConfig?.repeat && this._currentConfig?.repeat >= 1;
|
|
879
|
+
if (this._status === "PLAYING" && !canBeIntercepted) return;
|
|
880
|
+
const isRepeating = (this._currentConfig?.repeat ?? 0) >= 1;
|
|
659
881
|
const savedRepeatCount = isRepeating ? this._repeatCount : 0;
|
|
660
882
|
this.reset(false);
|
|
661
883
|
this._repeatCount = savedRepeatCount;
|
|
@@ -664,17 +886,20 @@ var Timeline = class {
|
|
|
664
886
|
return;
|
|
665
887
|
}
|
|
666
888
|
this._currentConfig = config;
|
|
889
|
+
if (this._repeatCount === 0) {
|
|
890
|
+
this._delay = (this._currentConfig.delay ?? 0) * 1e-3;
|
|
891
|
+
}
|
|
667
892
|
this._currentExecutionPlan = this._resolveExecutionPlan(
|
|
668
893
|
config.keyframes,
|
|
669
894
|
config.durations
|
|
670
895
|
);
|
|
671
|
-
const { keyMap
|
|
672
|
-
this._propertyKeyMap =
|
|
896
|
+
const { keyMap, values } = resolveGroup(config.from);
|
|
897
|
+
this._propertyKeyMap = keyMap;
|
|
673
898
|
this._state = values;
|
|
674
899
|
this._initialState = values;
|
|
675
900
|
this._status = "PLAYING";
|
|
676
901
|
this._clock.subscribe(this);
|
|
677
|
-
this.
|
|
902
|
+
this.notify();
|
|
678
903
|
}
|
|
679
904
|
pause() {
|
|
680
905
|
this._status = "PAUSED";
|
|
@@ -689,16 +914,14 @@ var Timeline = class {
|
|
|
689
914
|
this._status = "IDLE";
|
|
690
915
|
this._currentConfig = null;
|
|
691
916
|
this._masterTime = 0;
|
|
917
|
+
this._delay = 0;
|
|
692
918
|
this._state = [];
|
|
693
919
|
this._initialState = [];
|
|
694
920
|
this._propertyKeyMap = null;
|
|
695
|
-
this._segmentStartStates.clear();
|
|
696
921
|
this._currentExecutionPlan = null;
|
|
697
922
|
this._clock.unsubscribe(this);
|
|
698
923
|
this._repeatCount = 0;
|
|
699
|
-
if (notify)
|
|
700
|
-
this.notify();
|
|
701
|
-
}
|
|
924
|
+
if (notify) this.notify();
|
|
702
925
|
}
|
|
703
926
|
seek(targetTime) {
|
|
704
927
|
if (this._status === "PLAYING" || this._status === "ENDED") {
|
|
@@ -709,78 +932,50 @@ var Timeline = class {
|
|
|
709
932
|
this._state = this._calculateStateAtTime(seekTime, 0);
|
|
710
933
|
this.notify();
|
|
711
934
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
935
|
+
/**
|
|
936
|
+
* When timeline updates, subscribes on update callback.
|
|
937
|
+
* @param callback Subscription callback.
|
|
938
|
+
* @returns Unsubscribe.
|
|
939
|
+
*/
|
|
716
940
|
onUpdate(callback) {
|
|
717
941
|
this._onUpdateCallbacks.add(callback);
|
|
718
942
|
return () => {
|
|
719
943
|
this._onUpdateCallbacks.delete(callback);
|
|
720
944
|
};
|
|
721
945
|
}
|
|
722
|
-
update(dt) {
|
|
723
|
-
if (this._status !== "PLAYING") {
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
this._masterTime += dt;
|
|
727
|
-
if (this._masterTime >= this.duration) {
|
|
728
|
-
this._masterTime = this.duration;
|
|
729
|
-
}
|
|
730
|
-
this._state = this._calculateStateAtTime(this._masterTime, dt);
|
|
731
|
-
this.notify();
|
|
732
|
-
if (isEndOfAnimation(this._masterTime, this.duration)) {
|
|
733
|
-
this._repeatCount += 1;
|
|
734
|
-
if (!this._currentConfig) {
|
|
735
|
-
throw new Error(
|
|
736
|
-
`[Timeline] currentConfig can not be null when update(dt)`
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
|
|
740
|
-
if (noRepeat) {
|
|
741
|
-
this._status = "ENDED";
|
|
742
|
-
this._clock.unsubscribe(this);
|
|
743
|
-
this.notify();
|
|
744
|
-
} else {
|
|
745
|
-
this.play(this._currentConfig);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
946
|
};
|
|
750
|
-
function
|
|
751
|
-
return new
|
|
947
|
+
function rafTimeline(rootNode, clock) {
|
|
948
|
+
return new RafAniTimeline(rootNode, clock);
|
|
752
949
|
}
|
|
753
950
|
|
|
754
|
-
// src/ani/states.ts
|
|
951
|
+
// src/ani/raf/states.ts
|
|
755
952
|
function createStates(config) {
|
|
756
953
|
let State = config.initial;
|
|
757
|
-
let
|
|
954
|
+
let Timeline = rafTimeline(
|
|
758
955
|
config.states[State],
|
|
759
956
|
config.clock
|
|
760
957
|
);
|
|
761
958
|
const subs = /* @__PURE__ */ new Set();
|
|
762
|
-
const notify = (
|
|
959
|
+
const notify = (timeline) => {
|
|
763
960
|
for (const Sub of subs) {
|
|
764
|
-
Sub(
|
|
961
|
+
Sub(timeline);
|
|
765
962
|
}
|
|
766
963
|
};
|
|
767
964
|
return {
|
|
768
|
-
timeline: () =>
|
|
965
|
+
timeline: () => Timeline,
|
|
966
|
+
state: () => State,
|
|
769
967
|
onTimelineChange(callback) {
|
|
770
968
|
subs.add(callback);
|
|
771
969
|
return () => subs.delete(callback);
|
|
772
970
|
},
|
|
773
971
|
transitionTo(newState, timelineConfig, canBeIntercepted) {
|
|
774
|
-
if (!config.states[newState] || State === newState) {
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
972
|
const from = timelineConfig?.from ?? // 1. config
|
|
778
|
-
|
|
973
|
+
Timeline.getCurrentValue() ?? // 2. last value
|
|
779
974
|
config.initialFrom;
|
|
780
975
|
State = newState;
|
|
781
|
-
|
|
782
|
-
notify(
|
|
783
|
-
|
|
976
|
+
Timeline = rafTimeline(config.states[State], config.clock);
|
|
977
|
+
notify(Timeline);
|
|
978
|
+
Timeline.play(
|
|
784
979
|
{
|
|
785
980
|
...timelineConfig,
|
|
786
981
|
from
|
|
@@ -791,77 +986,6 @@ function createStates(config) {
|
|
|
791
986
|
};
|
|
792
987
|
}
|
|
793
988
|
|
|
794
|
-
// src/event/manager.ts
|
|
795
|
-
var EventManager = class _EventManager {
|
|
796
|
-
constructor(supportedEvents) {
|
|
797
|
-
this.supportedEvents = supportedEvents;
|
|
798
|
-
this._element = null;
|
|
799
|
-
this._animeGetter = null;
|
|
800
|
-
this.setAnimeGetter = (animeGetter) => {
|
|
801
|
-
this._animeGetter = animeGetter;
|
|
802
|
-
};
|
|
803
|
-
this.eventMap = /* @__PURE__ */ new Map();
|
|
804
|
-
this.withAnimeValue = (listener) => {
|
|
805
|
-
const withAnime = (e) => {
|
|
806
|
-
listener(this.animeGetter(), e);
|
|
807
|
-
};
|
|
808
|
-
return withAnime;
|
|
809
|
-
};
|
|
810
|
-
this.add = (eventName, listener, options) => {
|
|
811
|
-
const withAnime = this.withAnimeValue(listener);
|
|
812
|
-
this.eventMap.set(eventName, withAnime);
|
|
813
|
-
this.targetElement.addEventListener(
|
|
814
|
-
eventName,
|
|
815
|
-
this.eventMap.get(eventName),
|
|
816
|
-
options
|
|
817
|
-
);
|
|
818
|
-
};
|
|
819
|
-
this.cleanupOne = (eventName) => {
|
|
820
|
-
const removeListener = this.eventMap.get(eventName);
|
|
821
|
-
if (!removeListener) return false;
|
|
822
|
-
this.targetElement.removeEventListener(eventName, removeListener);
|
|
823
|
-
return true;
|
|
824
|
-
};
|
|
825
|
-
this.cleanupAll = () => {
|
|
826
|
-
const clearResponse = [];
|
|
827
|
-
for (const evtName of this.eventMap.keys()) {
|
|
828
|
-
const res = this.cleanupOne(evtName);
|
|
829
|
-
clearResponse.push(res);
|
|
830
|
-
}
|
|
831
|
-
return clearResponse.some((t) => t === false) === false;
|
|
832
|
-
};
|
|
833
|
-
this.attach = (handlers) => {
|
|
834
|
-
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
835
|
-
this.add(
|
|
836
|
-
_EventManager.getEvtKey(eventKey),
|
|
837
|
-
handler
|
|
838
|
-
);
|
|
839
|
-
});
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
get targetElement() {
|
|
843
|
-
if (!this._element) throw new Error("EventManger, bind element first");
|
|
844
|
-
return this._element;
|
|
845
|
-
}
|
|
846
|
-
get animeGetter() {
|
|
847
|
-
if (!this._animeGetter)
|
|
848
|
-
throw new Error("EventManager, animeGetter should be provided");
|
|
849
|
-
return this._animeGetter;
|
|
850
|
-
}
|
|
851
|
-
/**
|
|
852
|
-
* get pure `{event_name}`
|
|
853
|
-
* @param key onX`{event_name}`
|
|
854
|
-
*/
|
|
855
|
-
static getEvtKey(key) {
|
|
856
|
-
const removed = key.substring(2, key.length);
|
|
857
|
-
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
858
|
-
return Capitalized;
|
|
859
|
-
}
|
|
860
|
-
bind(element) {
|
|
861
|
-
this._element = element;
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
|
|
865
989
|
// src/style/create_sheet.ts
|
|
866
990
|
var TransformFunctionMap = {
|
|
867
991
|
// deg
|
|
@@ -949,40 +1073,286 @@ function createStyleSheet(animeStyleValue, resolver) {
|
|
|
949
1073
|
return styleAccumulator;
|
|
950
1074
|
}
|
|
951
1075
|
|
|
1076
|
+
// src/ani/waapi/compiler/resolver.ts
|
|
1077
|
+
function resolveStateAt(plan, initialFrom, targetTime, dt) {
|
|
1078
|
+
const { keyMap, values: initialValues } = resolveGroup(initialFrom);
|
|
1079
|
+
const rawResultState = resolvePlanState(
|
|
1080
|
+
plan,
|
|
1081
|
+
initialValues,
|
|
1082
|
+
keyMap,
|
|
1083
|
+
targetTime,
|
|
1084
|
+
dt
|
|
1085
|
+
);
|
|
1086
|
+
return resolveStateToGroup(rawResultState, keyMap);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// src/ani/waapi/compiler/timing_compiler.ts
|
|
1090
|
+
function compileTiming(timing) {
|
|
1091
|
+
if (!timing) {
|
|
1092
|
+
return "linear";
|
|
1093
|
+
}
|
|
1094
|
+
if (timing instanceof LinearTimingFunction) {
|
|
1095
|
+
return "linear";
|
|
1096
|
+
}
|
|
1097
|
+
if (timing instanceof BezierTimingFunction) {
|
|
1098
|
+
const { p2, p3 } = timing.opt;
|
|
1099
|
+
return `cubic-bezier(${p2.x}, ${p2.y}, ${p3.x}, ${p3.y})`;
|
|
1100
|
+
}
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// src/ani/waapi/compiler/keyframe_compiler.ts
|
|
1105
|
+
function compileToKeyframes(plan, initialFrom) {
|
|
1106
|
+
if (plan.length === 0) {
|
|
1107
|
+
return [];
|
|
1108
|
+
}
|
|
1109
|
+
const FPS = 60;
|
|
1110
|
+
const SAMPLE_RATE = 1 / FPS;
|
|
1111
|
+
const duration = Math.max(...plan.map((s) => s.endTime));
|
|
1112
|
+
if (duration === 0) {
|
|
1113
|
+
const state = resolveStateAt(plan, initialFrom, 0, SAMPLE_RATE);
|
|
1114
|
+
const style = createStyleSheet(state);
|
|
1115
|
+
return [
|
|
1116
|
+
{ offset: 0, ...style },
|
|
1117
|
+
{ offset: 1, ...style }
|
|
1118
|
+
];
|
|
1119
|
+
}
|
|
1120
|
+
const timePoints = /* @__PURE__ */ new Set([0, duration]);
|
|
1121
|
+
for (const seg of plan) {
|
|
1122
|
+
timePoints.add(seg.startTime);
|
|
1123
|
+
timePoints.add(seg.endTime);
|
|
1124
|
+
}
|
|
1125
|
+
const sortedTimes = Array.from(timePoints).sort((a2, b) => a2 - b);
|
|
1126
|
+
const keyframes = [];
|
|
1127
|
+
const getEasingForInterval = (t, nextT) => {
|
|
1128
|
+
const activeSegments = plan.filter(
|
|
1129
|
+
(s) => s.startTime <= t && s.endTime >= nextT
|
|
1130
|
+
);
|
|
1131
|
+
if (activeSegments.length === 0) return "linear";
|
|
1132
|
+
const timings = activeSegments.map((s) => s.node.props.timing).filter((t2) => t2 !== void 0);
|
|
1133
|
+
if (timings.length === 0) return "linear";
|
|
1134
|
+
const firstTiming = timings[0];
|
|
1135
|
+
const allSame = timings.every((t2) => t2 === firstTiming);
|
|
1136
|
+
if (allSame && firstTiming instanceof TimingFunction) {
|
|
1137
|
+
return compileTiming(firstTiming);
|
|
1138
|
+
}
|
|
1139
|
+
return null;
|
|
1140
|
+
};
|
|
1141
|
+
for (let i = 0; i < sortedTimes.length; i++) {
|
|
1142
|
+
const currT = sortedTimes[i];
|
|
1143
|
+
const state = resolveStateAt(plan, initialFrom, currT, SAMPLE_RATE);
|
|
1144
|
+
const style = createStyleSheet(state);
|
|
1145
|
+
const keyframe = {
|
|
1146
|
+
offset: currT / duration,
|
|
1147
|
+
...style
|
|
1148
|
+
};
|
|
1149
|
+
keyframes.push(keyframe);
|
|
1150
|
+
if (i < sortedTimes.length - 1) {
|
|
1151
|
+
const nextT = sortedTimes[i + 1];
|
|
1152
|
+
const easing = getEasingForInterval(currT, nextT);
|
|
1153
|
+
if (easing === null) {
|
|
1154
|
+
let sampleT = currT + SAMPLE_RATE;
|
|
1155
|
+
while (sampleT < nextT) {
|
|
1156
|
+
const sampleState = resolveStateAt(
|
|
1157
|
+
plan,
|
|
1158
|
+
initialFrom,
|
|
1159
|
+
sampleT,
|
|
1160
|
+
SAMPLE_RATE
|
|
1161
|
+
);
|
|
1162
|
+
const sampleStyle = createStyleSheet(
|
|
1163
|
+
sampleState
|
|
1164
|
+
);
|
|
1165
|
+
keyframes.push({
|
|
1166
|
+
offset: sampleT / duration,
|
|
1167
|
+
...sampleStyle,
|
|
1168
|
+
easing: "linear"
|
|
1169
|
+
});
|
|
1170
|
+
sampleT += SAMPLE_RATE;
|
|
1171
|
+
}
|
|
1172
|
+
keyframe.easing = "linear";
|
|
1173
|
+
} else {
|
|
1174
|
+
keyframe.easing = easing;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return keyframes;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/ani/waapi/timeline.ts
|
|
1182
|
+
var WebAniTimeline = class extends TimelineBase {
|
|
1183
|
+
constructor(rootNode) {
|
|
1184
|
+
super(rootNode);
|
|
1185
|
+
this._animation = null;
|
|
1186
|
+
this._keyframes = [];
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Plays animation.
|
|
1190
|
+
* @param target Target element.
|
|
1191
|
+
* @param config {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API Web Animations API} based config.
|
|
1192
|
+
*/
|
|
1193
|
+
play(target, config) {
|
|
1194
|
+
if (this._animation) {
|
|
1195
|
+
this._animation.cancel();
|
|
1196
|
+
}
|
|
1197
|
+
this._currentExecutionPlan = this._resolveExecutionPlan(
|
|
1198
|
+
config.keyframes,
|
|
1199
|
+
config.durations
|
|
1200
|
+
);
|
|
1201
|
+
this._keyframes = compileToKeyframes(
|
|
1202
|
+
this._currentExecutionPlan,
|
|
1203
|
+
config.from
|
|
1204
|
+
);
|
|
1205
|
+
if (this._keyframes.length === 0) {
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
const totalDurationMs = this._currentExecutionPlan.reduce(
|
|
1209
|
+
(max, seg) => Math.max(max, seg.endTime),
|
|
1210
|
+
0
|
|
1211
|
+
) * 1e3;
|
|
1212
|
+
const effect = new KeyframeEffect(target, this._keyframes, {
|
|
1213
|
+
duration: totalDurationMs,
|
|
1214
|
+
iterations: config.repeat ?? 1,
|
|
1215
|
+
delay: config.delay ?? 0,
|
|
1216
|
+
fill: "forwards"
|
|
1217
|
+
});
|
|
1218
|
+
this._animation = new Animation(effect, document.timeline);
|
|
1219
|
+
this._animation.play();
|
|
1220
|
+
return this._animation;
|
|
1221
|
+
}
|
|
1222
|
+
pause() {
|
|
1223
|
+
this._animation?.pause();
|
|
1224
|
+
}
|
|
1225
|
+
resume() {
|
|
1226
|
+
this._animation?.play();
|
|
1227
|
+
}
|
|
1228
|
+
reset() {
|
|
1229
|
+
this._animation?.cancel();
|
|
1230
|
+
this._animation = null;
|
|
1231
|
+
}
|
|
1232
|
+
seek(targetTime) {
|
|
1233
|
+
if (this._animation) {
|
|
1234
|
+
this._animation.currentTime = targetTime * 1e3;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Native animation object.
|
|
1239
|
+
*
|
|
1240
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Animation Animation}.
|
|
1241
|
+
*/
|
|
1242
|
+
get nativeAnimation() {
|
|
1243
|
+
return this._animation;
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
function webTimeline(rootNode) {
|
|
1247
|
+
return new WebAniTimeline(rootNode);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// src/event/manager.ts
|
|
1251
|
+
var EventManager = class _EventManager {
|
|
1252
|
+
constructor(supportedEvents) {
|
|
1253
|
+
this.supportedEvents = supportedEvents;
|
|
1254
|
+
this._element = null;
|
|
1255
|
+
this._animeGetter = null;
|
|
1256
|
+
this.setAnimeGetter = (animeGetter) => {
|
|
1257
|
+
this._animeGetter = animeGetter;
|
|
1258
|
+
};
|
|
1259
|
+
this.eventMap = /* @__PURE__ */ new Map();
|
|
1260
|
+
this.withAnimeValue = (listener) => {
|
|
1261
|
+
const withAnime = (e) => {
|
|
1262
|
+
listener(this.animeGetter(), e);
|
|
1263
|
+
};
|
|
1264
|
+
return withAnime;
|
|
1265
|
+
};
|
|
1266
|
+
this.add = (eventName, listener, options) => {
|
|
1267
|
+
const withAnime = this.withAnimeValue(listener);
|
|
1268
|
+
this.eventMap.set(eventName, withAnime);
|
|
1269
|
+
this.targetElement.addEventListener(
|
|
1270
|
+
eventName,
|
|
1271
|
+
this.eventMap.get(eventName),
|
|
1272
|
+
options
|
|
1273
|
+
);
|
|
1274
|
+
};
|
|
1275
|
+
this.cleanupOne = (eventName) => {
|
|
1276
|
+
const removeListener = this.eventMap.get(eventName);
|
|
1277
|
+
if (!removeListener) return false;
|
|
1278
|
+
this.targetElement.removeEventListener(eventName, removeListener);
|
|
1279
|
+
return true;
|
|
1280
|
+
};
|
|
1281
|
+
this.cleanupAll = () => {
|
|
1282
|
+
const clearResponse = [];
|
|
1283
|
+
for (const evtName of this.eventMap.keys()) {
|
|
1284
|
+
const res = this.cleanupOne(evtName);
|
|
1285
|
+
clearResponse.push(res);
|
|
1286
|
+
}
|
|
1287
|
+
return clearResponse.some((t) => t === false) === false;
|
|
1288
|
+
};
|
|
1289
|
+
this.attach = (handlers) => {
|
|
1290
|
+
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
1291
|
+
this.add(
|
|
1292
|
+
_EventManager.getEvtKey(eventKey),
|
|
1293
|
+
handler
|
|
1294
|
+
);
|
|
1295
|
+
});
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
get targetElement() {
|
|
1299
|
+
if (!this._element) throw new Error("EventManger, bind element first");
|
|
1300
|
+
return this._element;
|
|
1301
|
+
}
|
|
1302
|
+
get animeGetter() {
|
|
1303
|
+
if (!this._animeGetter)
|
|
1304
|
+
throw new Error("EventManager, animeGetter should be provided");
|
|
1305
|
+
return this._animeGetter;
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* get pure `{event_name}`
|
|
1309
|
+
* @param key onX`{event_name}`
|
|
1310
|
+
*/
|
|
1311
|
+
static getEvtKey(key) {
|
|
1312
|
+
const removed = key.substring(2, key.length);
|
|
1313
|
+
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
1314
|
+
return Capitalized;
|
|
1315
|
+
}
|
|
1316
|
+
bind(element) {
|
|
1317
|
+
this._element = element;
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
|
|
952
1321
|
// src/index.ts
|
|
953
1322
|
var a = {
|
|
954
1323
|
timing: T,
|
|
1324
|
+
dynamicTimeline: rafTimeline,
|
|
1325
|
+
timeline: webTimeline,
|
|
1326
|
+
/**
|
|
1327
|
+
* Create animation segment.
|
|
1328
|
+
*/
|
|
955
1329
|
ani,
|
|
956
|
-
|
|
1330
|
+
/**
|
|
1331
|
+
* Add delay
|
|
1332
|
+
*/
|
|
957
1333
|
delay,
|
|
958
1334
|
loop,
|
|
959
1335
|
parallel,
|
|
960
1336
|
sequence,
|
|
961
1337
|
stagger,
|
|
962
|
-
|
|
1338
|
+
createStates
|
|
963
1339
|
};
|
|
964
1340
|
// Annotate the CommonJS export names for ESM import in node:
|
|
965
1341
|
0 && (module.exports = {
|
|
966
1342
|
AnimationClock,
|
|
967
|
-
|
|
968
|
-
CompositionNode,
|
|
1343
|
+
BezierTimingFunction,
|
|
969
1344
|
EventManager,
|
|
970
1345
|
LinearTimingFunction,
|
|
971
|
-
|
|
972
|
-
SegmentNode,
|
|
973
|
-
SequenceNode,
|
|
974
|
-
StaggerNode,
|
|
1346
|
+
RafAniTimeline,
|
|
975
1347
|
T,
|
|
976
|
-
|
|
1348
|
+
TimelineBase,
|
|
977
1349
|
TimingFunction,
|
|
1350
|
+
WebAniTimeline,
|
|
978
1351
|
a,
|
|
979
|
-
|
|
1352
|
+
calculateSegmentState,
|
|
1353
|
+
compileToKeyframes,
|
|
980
1354
|
createStates,
|
|
981
1355
|
createStyleSheet,
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
parallel,
|
|
985
|
-
sequence,
|
|
986
|
-
stagger,
|
|
987
|
-
timeline
|
|
1356
|
+
rafTimeline,
|
|
1357
|
+
webTimeline
|
|
988
1358
|
});
|