@freestylejs/ani-core 1.1.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 +675 -401
- package/dist/index.d.cts +370 -270
- package/dist/index.d.ts +370 -270
- package/dist/index.js +667 -387
- package/package.json +1 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,39 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/utils/time/is_end.ts
|
|
2
|
+
function isEndOfAnimation(currentT, duration, tolerance = 1e-3) {
|
|
3
|
+
return currentT === duration || currentT - duration >= tolerance;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/ani/core/engine.ts
|
|
7
|
+
function calculateSegmentState(localTime, segmentDef, dt = 0) {
|
|
8
|
+
const t = Math.max(0, Math.min(localTime, segmentDef.duration));
|
|
9
|
+
const animeValues = [];
|
|
10
|
+
let allComplete = true;
|
|
11
|
+
const isMultipleTiming = Array.isArray(segmentDef.timing);
|
|
12
|
+
if (isMultipleTiming && segmentDef.timing.length !== segmentDef.from.length) {
|
|
13
|
+
throw new TypeError(
|
|
14
|
+
`[calculateSegmentState] timing does not correctly set. It requires multiple timing for ${segmentDef.from}, but received ${segmentDef.timing}`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
for (let i = 0; i < segmentDef.from.length; i++) {
|
|
18
|
+
const timingFunction = isMultipleTiming ? segmentDef.timing[i] : segmentDef.timing;
|
|
19
|
+
const animeResponse = timingFunction.step(t, {
|
|
20
|
+
dt,
|
|
21
|
+
from: segmentDef.from[i],
|
|
22
|
+
to: segmentDef.to[i],
|
|
23
|
+
duration: segmentDef.duration
|
|
24
|
+
});
|
|
25
|
+
animeValues.push(animeResponse.value);
|
|
26
|
+
if (!animeResponse.endOfAnimation) {
|
|
27
|
+
allComplete = false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
values: animeValues,
|
|
32
|
+
isComplete: allComplete || isEndOfAnimation(t, segmentDef.duration)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/nodes/base.ts
|
|
2
37
|
var AnimationNode = class {
|
|
3
38
|
constructor(id) {
|
|
4
39
|
if (id) {
|
|
@@ -28,42 +63,122 @@ var TimingFunction = class _TimingFunction {
|
|
|
28
63
|
}
|
|
29
64
|
};
|
|
30
65
|
|
|
31
|
-
// src/timing/linear.ts
|
|
32
|
-
var LinearTimingFunction = class extends TimingFunction {
|
|
33
|
-
step(time, context) {
|
|
34
|
-
const progress = context.duration === 0 ? 1 : Math.max(0, Math.min(time / context.duration, 1));
|
|
35
|
-
const value = context.from + (context.to - context.from) * progress;
|
|
36
|
-
return { value, endOfAnimation: time >= context.duration };
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
66
|
// src/timing/bezier.ts
|
|
67
|
+
var NEWTON_ITERATIONS = 4;
|
|
68
|
+
var NEWTON_MIN_SLOPE = 1e-3;
|
|
69
|
+
var SUBDIVISION_PRECISION = 1e-7;
|
|
70
|
+
var SUBDIVISION_MAX_ITERATIONS = 10;
|
|
71
|
+
var SAMPLE_TABLE_SIZE = 11;
|
|
72
|
+
var SAMPLE_STEP_SIZE = 1 / (SAMPLE_TABLE_SIZE - 1);
|
|
41
73
|
var BezierTimingFunction = class extends TimingFunction {
|
|
42
74
|
constructor(opt) {
|
|
43
75
|
super();
|
|
44
76
|
this.opt = opt;
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
77
|
+
this.sampleValues = null;
|
|
78
|
+
if (this.opt.p2.x !== this.opt.p2.y || this.opt.p3.x !== this.opt.p3.y) {
|
|
79
|
+
this.sampleValues = new Float32Array(SAMPLE_TABLE_SIZE);
|
|
80
|
+
for (let i = 0; i < SAMPLE_TABLE_SIZE; ++i) {
|
|
81
|
+
this.sampleValues[i] = this.calcBezier(
|
|
82
|
+
i * SAMPLE_STEP_SIZE,
|
|
83
|
+
this.opt.p2.x,
|
|
84
|
+
this.opt.p3.x
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
53
88
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
calcBezier(t, a1, a2) {
|
|
90
|
+
return ((1 - 3 * a2 + 3 * a1) * t + (3 * a2 - 6 * a1)) * t * t + 3 * a1 * t;
|
|
91
|
+
}
|
|
92
|
+
getSlope(t, a1, a2) {
|
|
93
|
+
return 3 * (1 - 3 * a2 + 3 * a1) * t * t + 2 * (3 * a2 - 6 * a1) * t + 3 * a1;
|
|
94
|
+
}
|
|
95
|
+
getTForX(x) {
|
|
96
|
+
const mX1 = this.opt.p2.x;
|
|
97
|
+
const mX2 = this.opt.p3.x;
|
|
98
|
+
let intervalStart = 0;
|
|
99
|
+
let currentSample = 1;
|
|
100
|
+
const lastSample = SAMPLE_TABLE_SIZE - 1;
|
|
101
|
+
for (; currentSample !== lastSample && this.sampleValues[currentSample] <= x; ++currentSample) {
|
|
102
|
+
intervalStart += SAMPLE_STEP_SIZE;
|
|
103
|
+
}
|
|
104
|
+
--currentSample;
|
|
105
|
+
const dist = (x - this.sampleValues[currentSample]) / (this.sampleValues[currentSample + 1] - this.sampleValues[currentSample]);
|
|
106
|
+
const guessForT = intervalStart + dist * SAMPLE_STEP_SIZE;
|
|
107
|
+
const initialSlope = this.getSlope(guessForT, mX1, mX2);
|
|
108
|
+
if (initialSlope >= NEWTON_MIN_SLOPE) {
|
|
109
|
+
return this.newtonRaphsonIterate(x, guessForT, mX1, mX2);
|
|
110
|
+
}
|
|
111
|
+
if (initialSlope === 0) {
|
|
112
|
+
return guessForT;
|
|
113
|
+
}
|
|
114
|
+
return this.binarySubdivide(
|
|
115
|
+
x,
|
|
116
|
+
intervalStart,
|
|
117
|
+
intervalStart + SAMPLE_STEP_SIZE,
|
|
118
|
+
mX1,
|
|
119
|
+
mX2
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
binarySubdivide(aX, aA, aB, mX1, mX2) {
|
|
123
|
+
let currentX;
|
|
124
|
+
let currentT;
|
|
125
|
+
let i = 0;
|
|
126
|
+
let currentA = aA;
|
|
127
|
+
let currentB = aB;
|
|
128
|
+
do {
|
|
129
|
+
currentT = currentA + (currentB - currentA) / 2;
|
|
130
|
+
currentX = this.calcBezier(currentT, mX1, mX2) - aX;
|
|
131
|
+
if (currentX > 0) {
|
|
132
|
+
currentB = currentT;
|
|
133
|
+
} else {
|
|
134
|
+
currentA = currentT;
|
|
135
|
+
}
|
|
136
|
+
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
|
|
137
|
+
return currentT;
|
|
138
|
+
}
|
|
139
|
+
newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
|
|
140
|
+
let guessT = aGuessT;
|
|
141
|
+
for (let i = 0; i < NEWTON_ITERATIONS; ++i) {
|
|
142
|
+
const currentSlope = this.getSlope(guessT, mX1, mX2);
|
|
143
|
+
if (currentSlope === 0) {
|
|
144
|
+
return guessT;
|
|
145
|
+
}
|
|
146
|
+
const currentX = this.calcBezier(guessT, mX1, mX2) - aX;
|
|
147
|
+
guessT -= currentX / currentSlope;
|
|
148
|
+
}
|
|
149
|
+
return guessT;
|
|
57
150
|
}
|
|
58
151
|
step(time, context) {
|
|
59
|
-
const
|
|
152
|
+
const { duration, from, to } = context;
|
|
153
|
+
if (duration === 0) {
|
|
154
|
+
return { value: to, endOfAnimation: true };
|
|
155
|
+
}
|
|
156
|
+
const x = Math.max(0, Math.min(time / duration, 1));
|
|
157
|
+
let easedT = x;
|
|
158
|
+
if (this.opt.p2.x !== this.opt.p2.y || this.opt.p3.x !== this.opt.p3.y) {
|
|
159
|
+
if (!this.sampleValues) {
|
|
160
|
+
}
|
|
161
|
+
const t = this.getTForX(x);
|
|
162
|
+
easedT = this.calcBezier(t, this.opt.p2.y, this.opt.p3.y);
|
|
163
|
+
}
|
|
164
|
+
const value = from + (to - from) * easedT;
|
|
165
|
+
const endOfAnimation = time >= duration;
|
|
60
166
|
return {
|
|
61
|
-
value
|
|
62
|
-
endOfAnimation
|
|
167
|
+
value,
|
|
168
|
+
endOfAnimation
|
|
63
169
|
};
|
|
64
170
|
}
|
|
65
171
|
};
|
|
66
172
|
|
|
173
|
+
// src/timing/linear.ts
|
|
174
|
+
var LinearTimingFunction = class extends TimingFunction {
|
|
175
|
+
step(time, context) {
|
|
176
|
+
const progress = context.duration === 0 ? 1 : Math.max(0, Math.min(time / context.duration, 1));
|
|
177
|
+
const value = context.from + (context.to - context.from) * progress;
|
|
178
|
+
return { value, endOfAnimation: time >= context.duration };
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
67
182
|
// src/timing/dynamic_spring.ts
|
|
68
183
|
var DynamicSpringTimingFunction = class extends TimingFunction {
|
|
69
184
|
constructor(opt) {
|
|
@@ -221,10 +336,38 @@ var T = {
|
|
|
221
336
|
/**
|
|
222
337
|
* Creates linear timing function instance.
|
|
223
338
|
*/
|
|
224
|
-
linear: () => new LinearTimingFunction()
|
|
339
|
+
linear: () => new LinearTimingFunction(),
|
|
340
|
+
/**
|
|
341
|
+
* Standard CSS 'ease' timing function (0.25, 0.1, 0.25, 1.0).
|
|
342
|
+
*/
|
|
343
|
+
ease: () => new BezierTimingFunction({
|
|
344
|
+
p2: { x: 0.25, y: 0.1 },
|
|
345
|
+
p3: { x: 0.25, y: 1 }
|
|
346
|
+
}),
|
|
347
|
+
/**
|
|
348
|
+
* Standard CSS 'ease-in' timing function (0.42, 0, 1.0, 1.0).
|
|
349
|
+
*/
|
|
350
|
+
easeIn: () => new BezierTimingFunction({
|
|
351
|
+
p2: { x: 0.42, y: 0 },
|
|
352
|
+
p3: { x: 1, y: 1 }
|
|
353
|
+
}),
|
|
354
|
+
/**
|
|
355
|
+
* Standard CSS 'ease-out' timing function (0, 0, 0.58, 1.0).
|
|
356
|
+
*/
|
|
357
|
+
easeOut: () => new BezierTimingFunction({
|
|
358
|
+
p2: { x: 0, y: 0 },
|
|
359
|
+
p3: { x: 0.58, y: 1 }
|
|
360
|
+
}),
|
|
361
|
+
/**
|
|
362
|
+
* Standard CSS 'ease-in-out' timing function (0.42, 0, 0.58, 1.0).
|
|
363
|
+
*/
|
|
364
|
+
easeInOut: () => new BezierTimingFunction({
|
|
365
|
+
p2: { x: 0.42, y: 0 },
|
|
366
|
+
p3: { x: 0.58, y: 1 }
|
|
367
|
+
})
|
|
225
368
|
};
|
|
226
369
|
|
|
227
|
-
// src/
|
|
370
|
+
// src/nodes/segment.ts
|
|
228
371
|
var SegmentNode = class extends AnimationNode {
|
|
229
372
|
constructor(props, id) {
|
|
230
373
|
super(id);
|
|
@@ -251,7 +394,7 @@ function ani(props, id) {
|
|
|
251
394
|
return new SegmentNode(props, id);
|
|
252
395
|
}
|
|
253
396
|
|
|
254
|
-
// src/
|
|
397
|
+
// src/nodes/composition.ts
|
|
255
398
|
var CompositionNode = class _CompositionNode extends AnimationNode {
|
|
256
399
|
constructor(children, timing, id) {
|
|
257
400
|
super(id);
|
|
@@ -280,12 +423,12 @@ var CompositionNode = class _CompositionNode extends AnimationNode {
|
|
|
280
423
|
}
|
|
281
424
|
};
|
|
282
425
|
|
|
283
|
-
// src/
|
|
426
|
+
// src/nodes/delay.ts
|
|
284
427
|
function delay(duration, id) {
|
|
285
428
|
return new SegmentNode({ to: {}, duration }, id);
|
|
286
429
|
}
|
|
287
430
|
|
|
288
|
-
// src/
|
|
431
|
+
// src/nodes/loop.ts
|
|
289
432
|
var LoopNode = class extends CompositionNode {
|
|
290
433
|
constructor(child, loopCount, timing, id) {
|
|
291
434
|
super([child], timing, id);
|
|
@@ -306,7 +449,7 @@ function loop(child, loopCount, timing, id) {
|
|
|
306
449
|
return new LoopNode(child, loopCount, timing, id);
|
|
307
450
|
}
|
|
308
451
|
|
|
309
|
-
// src/
|
|
452
|
+
// src/nodes/parallel.ts
|
|
310
453
|
var ParallelNode = class extends CompositionNode {
|
|
311
454
|
constructor(children, timing, id) {
|
|
312
455
|
const seenProperty = /* @__PURE__ */ new Set();
|
|
@@ -356,7 +499,7 @@ function parallel(children, timing, id) {
|
|
|
356
499
|
return new ParallelNode(children, timing, id);
|
|
357
500
|
}
|
|
358
501
|
|
|
359
|
-
// src/
|
|
502
|
+
// src/nodes/sequence.ts
|
|
360
503
|
var SequenceNode = class extends CompositionNode {
|
|
361
504
|
constructor(children, timing, id) {
|
|
362
505
|
super(children, timing, id);
|
|
@@ -375,12 +518,12 @@ function sequence(children, timing, id) {
|
|
|
375
518
|
return new SequenceNode(children, timing, id);
|
|
376
519
|
}
|
|
377
520
|
|
|
378
|
-
// src/
|
|
521
|
+
// src/nodes/stagger.ts
|
|
379
522
|
var StaggerNode = class extends CompositionNode {
|
|
380
|
-
constructor(children,
|
|
381
|
-
super(children,
|
|
523
|
+
constructor(children, offset, timing, id) {
|
|
524
|
+
super(children, timing, id);
|
|
382
525
|
this.type = "STAGGER";
|
|
383
|
-
this.offset =
|
|
526
|
+
this.offset = offset;
|
|
384
527
|
if (children.length === 0) {
|
|
385
528
|
this.duration = 0;
|
|
386
529
|
} else {
|
|
@@ -396,10 +539,81 @@ var StaggerNode = class extends CompositionNode {
|
|
|
396
539
|
}
|
|
397
540
|
}
|
|
398
541
|
};
|
|
399
|
-
function stagger(children,
|
|
400
|
-
return new StaggerNode(children,
|
|
542
|
+
function stagger(children, offset, timing, id) {
|
|
543
|
+
return new StaggerNode(children, offset, timing, id);
|
|
401
544
|
}
|
|
402
545
|
|
|
546
|
+
// src/ani/core/interface/timeline_interface.ts
|
|
547
|
+
var TimelineBase = class {
|
|
548
|
+
constructor(rootNode) {
|
|
549
|
+
this.rootNode = rootNode;
|
|
550
|
+
this._currentExecutionPlan = null;
|
|
551
|
+
this.duration = rootNode.duration;
|
|
552
|
+
this._baseExecutionPlan = this._constructExecutionPlan(rootNode);
|
|
553
|
+
this.play = this.play.bind(this);
|
|
554
|
+
this.pause = this.pause.bind(this);
|
|
555
|
+
this.seek = this.seek.bind(this);
|
|
556
|
+
this.reset = this.reset.bind(this);
|
|
557
|
+
this.resume = this.resume.bind(this);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* flatten the AST into a linear execution plan.
|
|
561
|
+
*/
|
|
562
|
+
_constructExecutionPlan(rootNode) {
|
|
563
|
+
const plan = [];
|
|
564
|
+
rootNode.construct(plan, 0);
|
|
565
|
+
return plan;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Merges the base plan with runtime dynamic overrides.
|
|
569
|
+
*/
|
|
570
|
+
_resolveExecutionPlan(keyframes, durations) {
|
|
571
|
+
if (!keyframes && !durations) {
|
|
572
|
+
return [...this._baseExecutionPlan];
|
|
573
|
+
}
|
|
574
|
+
const segmentNodes = this._baseExecutionPlan.filter(
|
|
575
|
+
(segment) => segment.node.type === "SEGMENT"
|
|
576
|
+
);
|
|
577
|
+
const segLength = segmentNodes.length;
|
|
578
|
+
if (keyframes && keyframes.length !== segLength) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
`[Timeline] Keyframe mismatch: Expected ${segLength}, received ${keyframes.length}.`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (durations && durations.length !== segLength) {
|
|
584
|
+
throw new Error(
|
|
585
|
+
`[Timeline] Duration mismatch: Expected ${segLength}, received ${durations.length}.`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const newPlan = [];
|
|
589
|
+
let keyframeIndex = 0;
|
|
590
|
+
for (const segment of this._baseExecutionPlan) {
|
|
591
|
+
if (segment.node.type === "SEGMENT") {
|
|
592
|
+
const dynamicTo = keyframes?.[keyframeIndex];
|
|
593
|
+
const dynamicDuration = durations?.[keyframeIndex];
|
|
594
|
+
const newSegmentProps = {
|
|
595
|
+
...segment.node.props,
|
|
596
|
+
...dynamicTo && dynamicTo !== "keep" && {
|
|
597
|
+
to: dynamicTo
|
|
598
|
+
},
|
|
599
|
+
...dynamicDuration && dynamicDuration !== "keep" && {
|
|
600
|
+
duration: dynamicDuration
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
const newSegment = new SegmentNode(
|
|
604
|
+
newSegmentProps,
|
|
605
|
+
segment.node.id
|
|
606
|
+
);
|
|
607
|
+
newPlan.push({ ...segment, node: newSegment });
|
|
608
|
+
keyframeIndex++;
|
|
609
|
+
} else {
|
|
610
|
+
newPlan.push({ ...segment });
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return newPlan;
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
403
617
|
// src/loop/clock.ts
|
|
404
618
|
var AnimationClock = class _AnimationClock {
|
|
405
619
|
constructor(maxDeltaTime) {
|
|
@@ -455,46 +669,86 @@ var AnimationClock = class _AnimationClock {
|
|
|
455
669
|
}
|
|
456
670
|
};
|
|
457
671
|
|
|
458
|
-
// src/
|
|
459
|
-
function
|
|
460
|
-
|
|
672
|
+
// src/ani/core/resolver.ts
|
|
673
|
+
function resolveGroup(group) {
|
|
674
|
+
if (Array.isArray(group)) {
|
|
675
|
+
return { keyMap: null, values: group };
|
|
676
|
+
}
|
|
677
|
+
const typedGroup = group;
|
|
678
|
+
const keys = Object.keys(typedGroup);
|
|
679
|
+
const keyMap = new Map(keys.map((key, i) => [key, i]));
|
|
680
|
+
const values = keys.map((key) => typedGroup[key]);
|
|
681
|
+
return { keyMap, values };
|
|
461
682
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const t = Math.max(0, Math.min(localTime, segmentDef.duration));
|
|
466
|
-
const animeValues = [];
|
|
467
|
-
let allComplete = true;
|
|
468
|
-
const isMultipleTiming = Array.isArray(segmentDef.timing);
|
|
469
|
-
if (isMultipleTiming && segmentDef.timing.length !== segmentDef.from.length) {
|
|
470
|
-
throw new TypeError(
|
|
471
|
-
`[calculateSegmentState] timing does not correctly set. It requires multiple timing for ${segmentDef.from}, but received ${segmentDef.timing}`
|
|
472
|
-
);
|
|
683
|
+
function resolveStateToGroup(state, keyMap) {
|
|
684
|
+
if (!keyMap) {
|
|
685
|
+
return state;
|
|
473
686
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
687
|
+
const group = {};
|
|
688
|
+
for (const [key, index] of keyMap.entries()) {
|
|
689
|
+
group[key] = state[index];
|
|
690
|
+
}
|
|
691
|
+
return group;
|
|
692
|
+
}
|
|
693
|
+
function resolvePlanState(plan, initialValues, keyMap, targetTime, dt = 0) {
|
|
694
|
+
const nextState = [...initialValues];
|
|
695
|
+
let stateAtLastStartTime = [...initialValues];
|
|
696
|
+
for (const segment of plan) {
|
|
697
|
+
if (targetTime < segment.startTime) {
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
stateAtLastStartTime = [...nextState];
|
|
701
|
+
const { keyMap: segKeyMap, values: toValues } = resolveGroup(
|
|
702
|
+
segment.node.props.to
|
|
703
|
+
);
|
|
704
|
+
const isRecordAni = keyMap !== null;
|
|
705
|
+
let fromValues = [];
|
|
706
|
+
const timings = [];
|
|
707
|
+
const t = segment.node.props.timing;
|
|
708
|
+
const isRecordTiming = t && !(t instanceof TimingFunction);
|
|
709
|
+
if (isRecordAni) {
|
|
710
|
+
for (const key of segKeyMap.keys()) {
|
|
711
|
+
const index = keyMap.get(key);
|
|
712
|
+
fromValues.push(stateAtLastStartTime[index]);
|
|
713
|
+
if (isRecordTiming) {
|
|
714
|
+
timings.push(t[key]);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
fromValues = stateAtLastStartTime;
|
|
719
|
+
}
|
|
720
|
+
const localTime = targetTime - segment.startTime;
|
|
721
|
+
const segmentDef = {
|
|
722
|
+
from: fromValues,
|
|
723
|
+
to: toValues,
|
|
724
|
+
duration: segment.node.duration,
|
|
725
|
+
// default fallback = linear
|
|
726
|
+
timing: isRecordAni && isRecordTiming ? timings : t ?? T.linear()
|
|
727
|
+
};
|
|
728
|
+
const result = calculateSegmentState(localTime, segmentDef, dt);
|
|
729
|
+
const finalValues = result.isComplete ? toValues : result.values;
|
|
730
|
+
if (isRecordAni) {
|
|
731
|
+
let i = 0;
|
|
732
|
+
for (const key of segKeyMap.keys()) {
|
|
733
|
+
const stateIndex = keyMap.get(key);
|
|
734
|
+
if (stateIndex !== void 0 && stateIndex !== -1) {
|
|
735
|
+
nextState[stateIndex] = finalValues[i];
|
|
736
|
+
}
|
|
737
|
+
i++;
|
|
738
|
+
}
|
|
739
|
+
} else {
|
|
740
|
+
for (let i = 0; i < finalValues.length; i++) {
|
|
741
|
+
nextState[i] = finalValues[i];
|
|
742
|
+
}
|
|
485
743
|
}
|
|
486
744
|
}
|
|
487
|
-
return
|
|
488
|
-
values: animeValues,
|
|
489
|
-
isComplete: allComplete || isEndOfAnimation(t, segmentDef.duration)
|
|
490
|
-
};
|
|
745
|
+
return nextState;
|
|
491
746
|
}
|
|
492
747
|
|
|
493
|
-
// src/ani/timeline.ts
|
|
494
|
-
var
|
|
748
|
+
// src/ani/raf/timeline.ts
|
|
749
|
+
var RafAniTimeline = class extends TimelineBase {
|
|
495
750
|
constructor(rootNode, clock) {
|
|
496
|
-
|
|
497
|
-
this._currentExecutionPlan = null;
|
|
751
|
+
super(rootNode);
|
|
498
752
|
this._masterTime = 0;
|
|
499
753
|
this._delay = 0;
|
|
500
754
|
this._status = "IDLE";
|
|
@@ -503,194 +757,86 @@ var Timeline = class {
|
|
|
503
757
|
this._initialState = [];
|
|
504
758
|
this._repeatCount = 0;
|
|
505
759
|
this._propertyKeyMap = null;
|
|
506
|
-
this._segmentStartStates = /* @__PURE__ */ new Map();
|
|
507
760
|
this._onUpdateCallbacks = /* @__PURE__ */ new Set();
|
|
508
|
-
this.duration = rootNode.duration;
|
|
509
|
-
this._baseExecutionPlan = this._constructExecutionPlan(rootNode);
|
|
510
761
|
this._clock = clock ?? AnimationClock.create();
|
|
511
|
-
this.play.bind(this);
|
|
512
|
-
this.pause.bind(this);
|
|
513
|
-
this.seek.bind(this);
|
|
514
|
-
this.resume.bind(this);
|
|
515
|
-
this.reset.bind(this);
|
|
762
|
+
this.play = this.play.bind(this);
|
|
763
|
+
this.pause = this.pause.bind(this);
|
|
764
|
+
this.seek = this.seek.bind(this);
|
|
765
|
+
this.resume = this.resume.bind(this);
|
|
766
|
+
this.reset = this.reset.bind(this);
|
|
516
767
|
}
|
|
517
|
-
/**
|
|
518
|
-
* Current animation running config.
|
|
519
|
-
*/
|
|
520
768
|
get currentConfig() {
|
|
521
769
|
return this._currentConfig;
|
|
522
770
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}
|
|
530
|
-
const keyMap = new Map(Object.keys(group).map((key, i) => [key, i]));
|
|
531
|
-
const values = Object.values(group);
|
|
532
|
-
return { keyMap, values };
|
|
771
|
+
getCurrentValue() {
|
|
772
|
+
if (this._state.length === 0) return null;
|
|
773
|
+
return resolveStateToGroup(
|
|
774
|
+
this._state,
|
|
775
|
+
this._propertyKeyMap
|
|
776
|
+
);
|
|
533
777
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
_resolveStateToGroup(state) {
|
|
538
|
-
if (!this._propertyKeyMap) {
|
|
539
|
-
return state;
|
|
540
|
-
}
|
|
541
|
-
const group = {};
|
|
542
|
-
let i = 0;
|
|
543
|
-
for (const key of this._propertyKeyMap.keys()) {
|
|
544
|
-
group[key] = state[i];
|
|
545
|
-
i++;
|
|
778
|
+
_calculateStateAtTime(targetTime, dt = 0) {
|
|
779
|
+
if (this._initialState.length === 0 || !this._currentExecutionPlan) {
|
|
780
|
+
return [];
|
|
546
781
|
}
|
|
547
|
-
return
|
|
782
|
+
return resolvePlanState(
|
|
783
|
+
this._currentExecutionPlan,
|
|
784
|
+
this._initialState,
|
|
785
|
+
this._propertyKeyMap,
|
|
786
|
+
// Using the class property directly
|
|
787
|
+
targetTime,
|
|
788
|
+
dt
|
|
789
|
+
);
|
|
548
790
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
791
|
+
notify() {
|
|
792
|
+
for (const subscriber of this._onUpdateCallbacks) {
|
|
793
|
+
subscriber({
|
|
794
|
+
state: resolveStateToGroup(
|
|
795
|
+
this._state,
|
|
796
|
+
this._propertyKeyMap
|
|
797
|
+
),
|
|
798
|
+
status: this._status
|
|
799
|
+
});
|
|
800
|
+
}
|
|
556
801
|
}
|
|
557
802
|
/**
|
|
558
|
-
*
|
|
803
|
+
* @private Internal clock subscription callback.
|
|
559
804
|
*/
|
|
560
|
-
|
|
561
|
-
if (this.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
if (targetTime < segment.startTime) {
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
if (!segment.node.props.timing) {
|
|
571
|
-
throw new Error(
|
|
572
|
-
`[Timeline] timing should be provided. Please specify timing using a.timing.(...). Check target segment: ${JSON.stringify(segment, null, 2)}.`,
|
|
573
|
-
{ cause: segment }
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
stateAtLastStartTime = [...nextState];
|
|
577
|
-
const { keyMap, values: toValues } = this._resolveGroup(
|
|
578
|
-
segment.node.props.to
|
|
579
|
-
);
|
|
580
|
-
const isRecordAni = this._propertyKeyMap !== null && keyMap !== null;
|
|
581
|
-
let fromValues = [];
|
|
582
|
-
const timings = [];
|
|
583
|
-
const t = segment.node.props.timing;
|
|
584
|
-
const isRecordTiming = t && !(t instanceof TimingFunction);
|
|
585
|
-
if (isRecordAni) {
|
|
586
|
-
for (const key of keyMap.keys()) {
|
|
587
|
-
const index = this._propertyKeyMap.get(key);
|
|
588
|
-
fromValues.push(stateAtLastStartTime[index]);
|
|
589
|
-
if (isRecordTiming) {
|
|
590
|
-
timings.push(
|
|
591
|
-
t[key]
|
|
592
|
-
);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
} else {
|
|
596
|
-
fromValues = stateAtLastStartTime;
|
|
597
|
-
}
|
|
598
|
-
let finalAnimeValues = [];
|
|
599
|
-
const localTime = targetTime - segment.startTime;
|
|
600
|
-
const segmentDef = {
|
|
601
|
-
from: fromValues,
|
|
602
|
-
to: toValues,
|
|
603
|
-
duration: segment.node.duration,
|
|
604
|
-
timing: isRecordAni && isRecordTiming ? timings : t
|
|
605
|
-
};
|
|
606
|
-
const segmentState = calculateSegmentState(
|
|
607
|
-
localTime,
|
|
608
|
-
segmentDef,
|
|
609
|
-
dt
|
|
610
|
-
);
|
|
611
|
-
if (segmentState.isComplete) {
|
|
612
|
-
finalAnimeValues = toValues;
|
|
805
|
+
update(dt) {
|
|
806
|
+
if (this._status !== "PLAYING") return;
|
|
807
|
+
if (this._delay > 0) {
|
|
808
|
+
this._delay -= dt;
|
|
809
|
+
if (this._delay < 0) {
|
|
810
|
+
dt = -this._delay;
|
|
811
|
+
this._delay = 0;
|
|
613
812
|
} else {
|
|
614
|
-
|
|
813
|
+
return;
|
|
615
814
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
815
|
+
}
|
|
816
|
+
this._masterTime += dt;
|
|
817
|
+
if (this._masterTime >= this.duration) this._masterTime = this.duration;
|
|
818
|
+
this._state = this._calculateStateAtTime(this._masterTime, dt);
|
|
819
|
+
this.notify();
|
|
820
|
+
if (isEndOfAnimation(this._masterTime, this.duration)) {
|
|
821
|
+
this._repeatCount += 1;
|
|
822
|
+
const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
|
|
823
|
+
if (noRepeat) {
|
|
824
|
+
this._status = "ENDED";
|
|
825
|
+
this._clock.unsubscribe(this);
|
|
826
|
+
this.notify();
|
|
626
827
|
} else {
|
|
627
|
-
|
|
628
|
-
nextState[i] = finalAnimeValues[i];
|
|
629
|
-
}
|
|
828
|
+
this.play(this._currentConfig);
|
|
630
829
|
}
|
|
631
830
|
}
|
|
632
|
-
return nextState;
|
|
633
|
-
}
|
|
634
|
-
_resolveExecutionPlan(keyframes, durations) {
|
|
635
|
-
if (!keyframes && !durations) {
|
|
636
|
-
return [...this._baseExecutionPlan];
|
|
637
|
-
}
|
|
638
|
-
const segmentNodes = this._baseExecutionPlan.filter(
|
|
639
|
-
(segment) => segment.node.type === "SEGMENT"
|
|
640
|
-
);
|
|
641
|
-
const segLength = segmentNodes.length;
|
|
642
|
-
if (keyframes && keyframes.length !== segLength) {
|
|
643
|
-
throw new Error(
|
|
644
|
-
`Timeline keyframe mismatch: Expected ${segLength} keyframes, but received ${keyframes.length}.`
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
if (durations && durations.length !== segLength) {
|
|
648
|
-
throw new Error(
|
|
649
|
-
`Timeline keyframe mismatch: Expected ${segLength} durations, but received ${durations.length}.`
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
|
-
const newPlan = [];
|
|
653
|
-
let keyframeIndex = 0;
|
|
654
|
-
for (const segment of this._baseExecutionPlan) {
|
|
655
|
-
if (segment.node.type === "SEGMENT") {
|
|
656
|
-
const dynamicTo = keyframes?.[keyframeIndex];
|
|
657
|
-
const dynamicDuration = durations?.[keyframeIndex];
|
|
658
|
-
const newSegmentProps = {
|
|
659
|
-
...segment.node.props,
|
|
660
|
-
// >> dynamic to
|
|
661
|
-
...dynamicTo && {
|
|
662
|
-
to: dynamicTo === "keep" ? segment.node.props.to : dynamicTo
|
|
663
|
-
},
|
|
664
|
-
// >> dynamic duration
|
|
665
|
-
...dynamicDuration && {
|
|
666
|
-
duration: dynamicDuration === "keep" ? segment.node.props.duration : dynamicDuration
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
const newSegment = new SegmentNode(
|
|
670
|
-
newSegmentProps,
|
|
671
|
-
segment.node.id
|
|
672
|
-
);
|
|
673
|
-
newPlan.push({ ...segment, node: newSegment });
|
|
674
|
-
keyframeIndex++;
|
|
675
|
-
} else {
|
|
676
|
-
newPlan.push({ ...segment });
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return newPlan;
|
|
680
|
-
}
|
|
681
|
-
notify() {
|
|
682
|
-
for (const subscriber of this._onUpdateCallbacks) {
|
|
683
|
-
subscriber({
|
|
684
|
-
state: this._resolveStateToGroup(this._state),
|
|
685
|
-
status: this._status
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
831
|
}
|
|
832
|
+
/**
|
|
833
|
+
* Plays animation.
|
|
834
|
+
* @param config {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame RequestAnimationFrame API} based config.
|
|
835
|
+
* @param canBeIntercepted if `true` animation can be intercepted even if already animation started.
|
|
836
|
+
*/
|
|
689
837
|
play(config, canBeIntercepted = true) {
|
|
690
|
-
if (this._status === "PLAYING" && !canBeIntercepted)
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
const isRepeating = this._currentConfig?.repeat && this._currentConfig?.repeat >= 1;
|
|
838
|
+
if (this._status === "PLAYING" && !canBeIntercepted) return;
|
|
839
|
+
const isRepeating = (this._currentConfig?.repeat ?? 0) >= 1;
|
|
694
840
|
const savedRepeatCount = isRepeating ? this._repeatCount : 0;
|
|
695
841
|
this.reset(false);
|
|
696
842
|
this._repeatCount = savedRepeatCount;
|
|
@@ -706,8 +852,8 @@ var Timeline = class {
|
|
|
706
852
|
config.keyframes,
|
|
707
853
|
config.durations
|
|
708
854
|
);
|
|
709
|
-
const { keyMap
|
|
710
|
-
this._propertyKeyMap =
|
|
855
|
+
const { keyMap, values } = resolveGroup(config.from);
|
|
856
|
+
this._propertyKeyMap = keyMap;
|
|
711
857
|
this._state = values;
|
|
712
858
|
this._initialState = values;
|
|
713
859
|
this._status = "PLAYING";
|
|
@@ -731,13 +877,10 @@ var Timeline = class {
|
|
|
731
877
|
this._state = [];
|
|
732
878
|
this._initialState = [];
|
|
733
879
|
this._propertyKeyMap = null;
|
|
734
|
-
this._segmentStartStates.clear();
|
|
735
880
|
this._currentExecutionPlan = null;
|
|
736
881
|
this._clock.unsubscribe(this);
|
|
737
882
|
this._repeatCount = 0;
|
|
738
|
-
if (notify)
|
|
739
|
-
this.notify();
|
|
740
|
-
}
|
|
883
|
+
if (notify) this.notify();
|
|
741
884
|
}
|
|
742
885
|
seek(targetTime) {
|
|
743
886
|
if (this._status === "PLAYING" || this._status === "ENDED") {
|
|
@@ -748,87 +891,50 @@ var Timeline = class {
|
|
|
748
891
|
this._state = this._calculateStateAtTime(seekTime, 0);
|
|
749
892
|
this.notify();
|
|
750
893
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
894
|
+
/**
|
|
895
|
+
* When timeline updates, subscribes on update callback.
|
|
896
|
+
* @param callback Subscription callback.
|
|
897
|
+
* @returns Unsubscribe.
|
|
898
|
+
*/
|
|
755
899
|
onUpdate(callback) {
|
|
756
900
|
this._onUpdateCallbacks.add(callback);
|
|
757
901
|
return () => {
|
|
758
902
|
this._onUpdateCallbacks.delete(callback);
|
|
759
903
|
};
|
|
760
904
|
}
|
|
761
|
-
update(dt) {
|
|
762
|
-
if (this._status !== "PLAYING") {
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
if (this._delay > 0) {
|
|
766
|
-
this._delay -= dt;
|
|
767
|
-
if (this._delay < 0) {
|
|
768
|
-
dt = -this._delay;
|
|
769
|
-
this._delay = 0;
|
|
770
|
-
} else {
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
this._masterTime += dt;
|
|
775
|
-
if (this._masterTime >= this.duration) {
|
|
776
|
-
this._masterTime = this.duration;
|
|
777
|
-
}
|
|
778
|
-
this._state = this._calculateStateAtTime(this._masterTime, dt);
|
|
779
|
-
this.notify();
|
|
780
|
-
if (isEndOfAnimation(this._masterTime, this.duration)) {
|
|
781
|
-
this._repeatCount += 1;
|
|
782
|
-
if (!this._currentConfig) {
|
|
783
|
-
throw new Error(
|
|
784
|
-
`[Timeline] currentConfig can not be null when update(dt)`
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
|
|
788
|
-
if (noRepeat) {
|
|
789
|
-
this._status = "ENDED";
|
|
790
|
-
this._clock.unsubscribe(this);
|
|
791
|
-
this.notify();
|
|
792
|
-
} else {
|
|
793
|
-
this.play(this._currentConfig);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
905
|
};
|
|
798
|
-
function
|
|
799
|
-
return new
|
|
906
|
+
function rafTimeline(rootNode, clock) {
|
|
907
|
+
return new RafAniTimeline(rootNode, clock);
|
|
800
908
|
}
|
|
801
909
|
|
|
802
|
-
// src/ani/states.ts
|
|
910
|
+
// src/ani/raf/states.ts
|
|
803
911
|
function createStates(config) {
|
|
804
912
|
let State = config.initial;
|
|
805
|
-
let
|
|
913
|
+
let Timeline = rafTimeline(
|
|
806
914
|
config.states[State],
|
|
807
915
|
config.clock
|
|
808
916
|
);
|
|
809
917
|
const subs = /* @__PURE__ */ new Set();
|
|
810
|
-
const notify = (
|
|
918
|
+
const notify = (timeline) => {
|
|
811
919
|
for (const Sub of subs) {
|
|
812
|
-
Sub(
|
|
920
|
+
Sub(timeline);
|
|
813
921
|
}
|
|
814
922
|
};
|
|
815
923
|
return {
|
|
816
|
-
timeline: () =>
|
|
924
|
+
timeline: () => Timeline,
|
|
925
|
+
state: () => State,
|
|
817
926
|
onTimelineChange(callback) {
|
|
818
927
|
subs.add(callback);
|
|
819
928
|
return () => subs.delete(callback);
|
|
820
929
|
},
|
|
821
930
|
transitionTo(newState, timelineConfig, canBeIntercepted) {
|
|
822
|
-
if (!config.states[newState] || State === newState) {
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
931
|
const from = timelineConfig?.from ?? // 1. config
|
|
826
|
-
|
|
932
|
+
Timeline.getCurrentValue() ?? // 2. last value
|
|
827
933
|
config.initialFrom;
|
|
828
934
|
State = newState;
|
|
829
|
-
|
|
830
|
-
notify(
|
|
831
|
-
|
|
935
|
+
Timeline = rafTimeline(config.states[State], config.clock);
|
|
936
|
+
notify(Timeline);
|
|
937
|
+
Timeline.play(
|
|
832
938
|
{
|
|
833
939
|
...timelineConfig,
|
|
834
940
|
from
|
|
@@ -839,77 +945,6 @@ function createStates(config) {
|
|
|
839
945
|
};
|
|
840
946
|
}
|
|
841
947
|
|
|
842
|
-
// src/event/manager.ts
|
|
843
|
-
var EventManager = class _EventManager {
|
|
844
|
-
constructor(supportedEvents) {
|
|
845
|
-
this.supportedEvents = supportedEvents;
|
|
846
|
-
this._element = null;
|
|
847
|
-
this._animeGetter = null;
|
|
848
|
-
this.setAnimeGetter = (animeGetter) => {
|
|
849
|
-
this._animeGetter = animeGetter;
|
|
850
|
-
};
|
|
851
|
-
this.eventMap = /* @__PURE__ */ new Map();
|
|
852
|
-
this.withAnimeValue = (listener) => {
|
|
853
|
-
const withAnime = (e) => {
|
|
854
|
-
listener(this.animeGetter(), e);
|
|
855
|
-
};
|
|
856
|
-
return withAnime;
|
|
857
|
-
};
|
|
858
|
-
this.add = (eventName, listener, options) => {
|
|
859
|
-
const withAnime = this.withAnimeValue(listener);
|
|
860
|
-
this.eventMap.set(eventName, withAnime);
|
|
861
|
-
this.targetElement.addEventListener(
|
|
862
|
-
eventName,
|
|
863
|
-
this.eventMap.get(eventName),
|
|
864
|
-
options
|
|
865
|
-
);
|
|
866
|
-
};
|
|
867
|
-
this.cleanupOne = (eventName) => {
|
|
868
|
-
const removeListener = this.eventMap.get(eventName);
|
|
869
|
-
if (!removeListener) return false;
|
|
870
|
-
this.targetElement.removeEventListener(eventName, removeListener);
|
|
871
|
-
return true;
|
|
872
|
-
};
|
|
873
|
-
this.cleanupAll = () => {
|
|
874
|
-
const clearResponse = [];
|
|
875
|
-
for (const evtName of this.eventMap.keys()) {
|
|
876
|
-
const res = this.cleanupOne(evtName);
|
|
877
|
-
clearResponse.push(res);
|
|
878
|
-
}
|
|
879
|
-
return clearResponse.some((t) => t === false) === false;
|
|
880
|
-
};
|
|
881
|
-
this.attach = (handlers) => {
|
|
882
|
-
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
883
|
-
this.add(
|
|
884
|
-
_EventManager.getEvtKey(eventKey),
|
|
885
|
-
handler
|
|
886
|
-
);
|
|
887
|
-
});
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
get targetElement() {
|
|
891
|
-
if (!this._element) throw new Error("EventManger, bind element first");
|
|
892
|
-
return this._element;
|
|
893
|
-
}
|
|
894
|
-
get animeGetter() {
|
|
895
|
-
if (!this._animeGetter)
|
|
896
|
-
throw new Error("EventManager, animeGetter should be provided");
|
|
897
|
-
return this._animeGetter;
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* get pure `{event_name}`
|
|
901
|
-
* @param key onX`{event_name}`
|
|
902
|
-
*/
|
|
903
|
-
static getEvtKey(key) {
|
|
904
|
-
const removed = key.substring(2, key.length);
|
|
905
|
-
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
906
|
-
return Capitalized;
|
|
907
|
-
}
|
|
908
|
-
bind(element) {
|
|
909
|
-
this._element = element;
|
|
910
|
-
}
|
|
911
|
-
};
|
|
912
|
-
|
|
913
948
|
// src/style/create_sheet.ts
|
|
914
949
|
var TransformFunctionMap = {
|
|
915
950
|
// deg
|
|
@@ -997,40 +1032,285 @@ function createStyleSheet(animeStyleValue, resolver) {
|
|
|
997
1032
|
return styleAccumulator;
|
|
998
1033
|
}
|
|
999
1034
|
|
|
1035
|
+
// src/ani/waapi/compiler/resolver.ts
|
|
1036
|
+
function resolveStateAt(plan, initialFrom, targetTime, dt) {
|
|
1037
|
+
const { keyMap, values: initialValues } = resolveGroup(initialFrom);
|
|
1038
|
+
const rawResultState = resolvePlanState(
|
|
1039
|
+
plan,
|
|
1040
|
+
initialValues,
|
|
1041
|
+
keyMap,
|
|
1042
|
+
targetTime,
|
|
1043
|
+
dt
|
|
1044
|
+
);
|
|
1045
|
+
return resolveStateToGroup(rawResultState, keyMap);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/ani/waapi/compiler/timing_compiler.ts
|
|
1049
|
+
function compileTiming(timing) {
|
|
1050
|
+
if (!timing) {
|
|
1051
|
+
return "linear";
|
|
1052
|
+
}
|
|
1053
|
+
if (timing instanceof LinearTimingFunction) {
|
|
1054
|
+
return "linear";
|
|
1055
|
+
}
|
|
1056
|
+
if (timing instanceof BezierTimingFunction) {
|
|
1057
|
+
const { p2, p3 } = timing.opt;
|
|
1058
|
+
return `cubic-bezier(${p2.x}, ${p2.y}, ${p3.x}, ${p3.y})`;
|
|
1059
|
+
}
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/ani/waapi/compiler/keyframe_compiler.ts
|
|
1064
|
+
function compileToKeyframes(plan, initialFrom) {
|
|
1065
|
+
if (plan.length === 0) {
|
|
1066
|
+
return [];
|
|
1067
|
+
}
|
|
1068
|
+
const FPS = 60;
|
|
1069
|
+
const SAMPLE_RATE = 1 / FPS;
|
|
1070
|
+
const duration = Math.max(...plan.map((s) => s.endTime));
|
|
1071
|
+
if (duration === 0) {
|
|
1072
|
+
const state = resolveStateAt(plan, initialFrom, 0, SAMPLE_RATE);
|
|
1073
|
+
const style = createStyleSheet(state);
|
|
1074
|
+
return [
|
|
1075
|
+
{ offset: 0, ...style },
|
|
1076
|
+
{ offset: 1, ...style }
|
|
1077
|
+
];
|
|
1078
|
+
}
|
|
1079
|
+
const timePoints = /* @__PURE__ */ new Set([0, duration]);
|
|
1080
|
+
for (const seg of plan) {
|
|
1081
|
+
timePoints.add(seg.startTime);
|
|
1082
|
+
timePoints.add(seg.endTime);
|
|
1083
|
+
}
|
|
1084
|
+
const sortedTimes = Array.from(timePoints).sort((a2, b) => a2 - b);
|
|
1085
|
+
const keyframes = [];
|
|
1086
|
+
const getEasingForInterval = (t, nextT) => {
|
|
1087
|
+
const activeSegments = plan.filter(
|
|
1088
|
+
(s) => s.startTime <= t && s.endTime >= nextT
|
|
1089
|
+
);
|
|
1090
|
+
if (activeSegments.length === 0) return "linear";
|
|
1091
|
+
const timings = activeSegments.map((s) => s.node.props.timing).filter((t2) => t2 !== void 0);
|
|
1092
|
+
if (timings.length === 0) return "linear";
|
|
1093
|
+
const firstTiming = timings[0];
|
|
1094
|
+
const allSame = timings.every((t2) => t2 === firstTiming);
|
|
1095
|
+
if (allSame && firstTiming instanceof TimingFunction) {
|
|
1096
|
+
return compileTiming(firstTiming);
|
|
1097
|
+
}
|
|
1098
|
+
return null;
|
|
1099
|
+
};
|
|
1100
|
+
for (let i = 0; i < sortedTimes.length; i++) {
|
|
1101
|
+
const currT = sortedTimes[i];
|
|
1102
|
+
const state = resolveStateAt(plan, initialFrom, currT, SAMPLE_RATE);
|
|
1103
|
+
const style = createStyleSheet(state);
|
|
1104
|
+
const keyframe = {
|
|
1105
|
+
offset: currT / duration,
|
|
1106
|
+
...style
|
|
1107
|
+
};
|
|
1108
|
+
keyframes.push(keyframe);
|
|
1109
|
+
if (i < sortedTimes.length - 1) {
|
|
1110
|
+
const nextT = sortedTimes[i + 1];
|
|
1111
|
+
const easing = getEasingForInterval(currT, nextT);
|
|
1112
|
+
if (easing === null) {
|
|
1113
|
+
let sampleT = currT + SAMPLE_RATE;
|
|
1114
|
+
while (sampleT < nextT) {
|
|
1115
|
+
const sampleState = resolveStateAt(
|
|
1116
|
+
plan,
|
|
1117
|
+
initialFrom,
|
|
1118
|
+
sampleT,
|
|
1119
|
+
SAMPLE_RATE
|
|
1120
|
+
);
|
|
1121
|
+
const sampleStyle = createStyleSheet(
|
|
1122
|
+
sampleState
|
|
1123
|
+
);
|
|
1124
|
+
keyframes.push({
|
|
1125
|
+
offset: sampleT / duration,
|
|
1126
|
+
...sampleStyle,
|
|
1127
|
+
easing: "linear"
|
|
1128
|
+
});
|
|
1129
|
+
sampleT += SAMPLE_RATE;
|
|
1130
|
+
}
|
|
1131
|
+
keyframe.easing = "linear";
|
|
1132
|
+
} else {
|
|
1133
|
+
keyframe.easing = easing;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return keyframes;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// src/ani/waapi/timeline.ts
|
|
1141
|
+
var WebAniTimeline = class extends TimelineBase {
|
|
1142
|
+
constructor(rootNode) {
|
|
1143
|
+
super(rootNode);
|
|
1144
|
+
this._animation = null;
|
|
1145
|
+
this._keyframes = [];
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Plays animation.
|
|
1149
|
+
* @param target Target element.
|
|
1150
|
+
* @param config {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API Web Animations API} based config.
|
|
1151
|
+
*/
|
|
1152
|
+
play(target, config) {
|
|
1153
|
+
if (this._animation) {
|
|
1154
|
+
this._animation.cancel();
|
|
1155
|
+
}
|
|
1156
|
+
this._currentExecutionPlan = this._resolveExecutionPlan(
|
|
1157
|
+
config.keyframes,
|
|
1158
|
+
config.durations
|
|
1159
|
+
);
|
|
1160
|
+
this._keyframes = compileToKeyframes(
|
|
1161
|
+
this._currentExecutionPlan,
|
|
1162
|
+
config.from
|
|
1163
|
+
);
|
|
1164
|
+
if (this._keyframes.length === 0) {
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
const totalDurationMs = this._currentExecutionPlan.reduce(
|
|
1168
|
+
(max, seg) => Math.max(max, seg.endTime),
|
|
1169
|
+
0
|
|
1170
|
+
) * 1e3;
|
|
1171
|
+
const effect = new KeyframeEffect(target, this._keyframes, {
|
|
1172
|
+
duration: totalDurationMs,
|
|
1173
|
+
iterations: config.repeat ?? 1,
|
|
1174
|
+
delay: config.delay ?? 0,
|
|
1175
|
+
fill: "forwards"
|
|
1176
|
+
});
|
|
1177
|
+
this._animation = new Animation(effect, document.timeline);
|
|
1178
|
+
this._animation.play();
|
|
1179
|
+
return this._animation;
|
|
1180
|
+
}
|
|
1181
|
+
pause() {
|
|
1182
|
+
this._animation?.pause();
|
|
1183
|
+
}
|
|
1184
|
+
resume() {
|
|
1185
|
+
this._animation?.play();
|
|
1186
|
+
}
|
|
1187
|
+
reset() {
|
|
1188
|
+
this._animation?.cancel();
|
|
1189
|
+
this._animation = null;
|
|
1190
|
+
}
|
|
1191
|
+
seek(targetTime) {
|
|
1192
|
+
if (this._animation) {
|
|
1193
|
+
this._animation.currentTime = targetTime * 1e3;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Native animation object.
|
|
1198
|
+
*
|
|
1199
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Animation Animation}.
|
|
1200
|
+
*/
|
|
1201
|
+
get nativeAnimation() {
|
|
1202
|
+
return this._animation;
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
function webTimeline(rootNode) {
|
|
1206
|
+
return new WebAniTimeline(rootNode);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// src/event/manager.ts
|
|
1210
|
+
var EventManager = class _EventManager {
|
|
1211
|
+
constructor(supportedEvents) {
|
|
1212
|
+
this.supportedEvents = supportedEvents;
|
|
1213
|
+
this._element = null;
|
|
1214
|
+
this._animeGetter = null;
|
|
1215
|
+
this.setAnimeGetter = (animeGetter) => {
|
|
1216
|
+
this._animeGetter = animeGetter;
|
|
1217
|
+
};
|
|
1218
|
+
this.eventMap = /* @__PURE__ */ new Map();
|
|
1219
|
+
this.withAnimeValue = (listener) => {
|
|
1220
|
+
const withAnime = (e) => {
|
|
1221
|
+
listener(this.animeGetter(), e);
|
|
1222
|
+
};
|
|
1223
|
+
return withAnime;
|
|
1224
|
+
};
|
|
1225
|
+
this.add = (eventName, listener, options) => {
|
|
1226
|
+
const withAnime = this.withAnimeValue(listener);
|
|
1227
|
+
this.eventMap.set(eventName, withAnime);
|
|
1228
|
+
this.targetElement.addEventListener(
|
|
1229
|
+
eventName,
|
|
1230
|
+
this.eventMap.get(eventName),
|
|
1231
|
+
options
|
|
1232
|
+
);
|
|
1233
|
+
};
|
|
1234
|
+
this.cleanupOne = (eventName) => {
|
|
1235
|
+
const removeListener = this.eventMap.get(eventName);
|
|
1236
|
+
if (!removeListener) return false;
|
|
1237
|
+
this.targetElement.removeEventListener(eventName, removeListener);
|
|
1238
|
+
return true;
|
|
1239
|
+
};
|
|
1240
|
+
this.cleanupAll = () => {
|
|
1241
|
+
const clearResponse = [];
|
|
1242
|
+
for (const evtName of this.eventMap.keys()) {
|
|
1243
|
+
const res = this.cleanupOne(evtName);
|
|
1244
|
+
clearResponse.push(res);
|
|
1245
|
+
}
|
|
1246
|
+
return clearResponse.some((t) => t === false) === false;
|
|
1247
|
+
};
|
|
1248
|
+
this.attach = (handlers) => {
|
|
1249
|
+
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
1250
|
+
this.add(
|
|
1251
|
+
_EventManager.getEvtKey(eventKey),
|
|
1252
|
+
handler
|
|
1253
|
+
);
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
get targetElement() {
|
|
1258
|
+
if (!this._element) throw new Error("EventManger, bind element first");
|
|
1259
|
+
return this._element;
|
|
1260
|
+
}
|
|
1261
|
+
get animeGetter() {
|
|
1262
|
+
if (!this._animeGetter)
|
|
1263
|
+
throw new Error("EventManager, animeGetter should be provided");
|
|
1264
|
+
return this._animeGetter;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* get pure `{event_name}`
|
|
1268
|
+
* @param key onX`{event_name}`
|
|
1269
|
+
*/
|
|
1270
|
+
static getEvtKey(key) {
|
|
1271
|
+
const removed = key.substring(2, key.length);
|
|
1272
|
+
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
1273
|
+
return Capitalized;
|
|
1274
|
+
}
|
|
1275
|
+
bind(element) {
|
|
1276
|
+
this._element = element;
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1000
1280
|
// src/index.ts
|
|
1001
1281
|
var a = {
|
|
1002
1282
|
timing: T,
|
|
1283
|
+
dynamicTimeline: rafTimeline,
|
|
1284
|
+
timeline: webTimeline,
|
|
1285
|
+
/**
|
|
1286
|
+
* Create animation segment.
|
|
1287
|
+
*/
|
|
1003
1288
|
ani,
|
|
1004
|
-
|
|
1289
|
+
/**
|
|
1290
|
+
* Add delay
|
|
1291
|
+
*/
|
|
1005
1292
|
delay,
|
|
1006
1293
|
loop,
|
|
1007
1294
|
parallel,
|
|
1008
1295
|
sequence,
|
|
1009
1296
|
stagger,
|
|
1010
|
-
|
|
1297
|
+
createStates
|
|
1011
1298
|
};
|
|
1012
1299
|
export {
|
|
1013
1300
|
AnimationClock,
|
|
1014
|
-
|
|
1015
|
-
CompositionNode,
|
|
1301
|
+
BezierTimingFunction,
|
|
1016
1302
|
EventManager,
|
|
1017
1303
|
LinearTimingFunction,
|
|
1018
|
-
|
|
1019
|
-
SegmentNode,
|
|
1020
|
-
SequenceNode,
|
|
1021
|
-
StaggerNode,
|
|
1304
|
+
RafAniTimeline,
|
|
1022
1305
|
T,
|
|
1023
|
-
|
|
1306
|
+
TimelineBase,
|
|
1024
1307
|
TimingFunction,
|
|
1308
|
+
WebAniTimeline,
|
|
1025
1309
|
a,
|
|
1026
|
-
|
|
1310
|
+
calculateSegmentState,
|
|
1311
|
+
compileToKeyframes,
|
|
1027
1312
|
createStates,
|
|
1028
1313
|
createStyleSheet,
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
parallel,
|
|
1032
|
-
sequence,
|
|
1033
|
-
stagger,
|
|
1034
|
-
timeline
|
|
1314
|
+
rafTimeline,
|
|
1315
|
+
webTimeline
|
|
1035
1316
|
};
|
|
1036
|
-
//# sourceMappingURL=index.js.map
|