@freestylejs/ani-core 1.1.0 → 1.2.1
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 +681 -404
- package/dist/index.d.cts +371 -271
- package/dist/index.d.ts +371 -271
- package/dist/index.js +673 -390
- 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,196 +757,89 @@ 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
|
-
this.
|
|
841
|
+
const isPlaying = this._status === "PLAYING";
|
|
842
|
+
this.reset(false, !isPlaying);
|
|
696
843
|
this._repeatCount = savedRepeatCount;
|
|
697
844
|
if (isRepeating && this._repeatCount >= config.repeat) {
|
|
698
845
|
this._repeatCount = 0;
|
|
@@ -706,8 +853,8 @@ var Timeline = class {
|
|
|
706
853
|
config.keyframes,
|
|
707
854
|
config.durations
|
|
708
855
|
);
|
|
709
|
-
const { keyMap
|
|
710
|
-
this._propertyKeyMap =
|
|
856
|
+
const { keyMap, values } = resolveGroup(config.from);
|
|
857
|
+
this._propertyKeyMap = keyMap;
|
|
711
858
|
this._state = values;
|
|
712
859
|
this._initialState = values;
|
|
713
860
|
this._status = "PLAYING";
|
|
@@ -723,7 +870,7 @@ var Timeline = class {
|
|
|
723
870
|
this._status = "PLAYING";
|
|
724
871
|
this._clock.subscribe(this);
|
|
725
872
|
}
|
|
726
|
-
reset(notify = true) {
|
|
873
|
+
reset(notify = true, unsubscribeClock = true) {
|
|
727
874
|
this._status = "IDLE";
|
|
728
875
|
this._currentConfig = null;
|
|
729
876
|
this._masterTime = 0;
|
|
@@ -731,13 +878,12 @@ var Timeline = class {
|
|
|
731
878
|
this._state = [];
|
|
732
879
|
this._initialState = [];
|
|
733
880
|
this._propertyKeyMap = null;
|
|
734
|
-
this._segmentStartStates.clear();
|
|
735
881
|
this._currentExecutionPlan = null;
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
if (notify) {
|
|
739
|
-
this.notify();
|
|
882
|
+
if (unsubscribeClock) {
|
|
883
|
+
this._clock.unsubscribe(this);
|
|
740
884
|
}
|
|
885
|
+
this._repeatCount = 0;
|
|
886
|
+
if (notify) this.notify();
|
|
741
887
|
}
|
|
742
888
|
seek(targetTime) {
|
|
743
889
|
if (this._status === "PLAYING" || this._status === "ENDED") {
|
|
@@ -748,87 +894,50 @@ var Timeline = class {
|
|
|
748
894
|
this._state = this._calculateStateAtTime(seekTime, 0);
|
|
749
895
|
this.notify();
|
|
750
896
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
897
|
+
/**
|
|
898
|
+
* When timeline updates, subscribes on update callback.
|
|
899
|
+
* @param callback Subscription callback.
|
|
900
|
+
* @returns Unsubscribe.
|
|
901
|
+
*/
|
|
755
902
|
onUpdate(callback) {
|
|
756
903
|
this._onUpdateCallbacks.add(callback);
|
|
757
904
|
return () => {
|
|
758
905
|
this._onUpdateCallbacks.delete(callback);
|
|
759
906
|
};
|
|
760
907
|
}
|
|
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
908
|
};
|
|
798
|
-
function
|
|
799
|
-
return new
|
|
909
|
+
function rafTimeline(rootNode, clock) {
|
|
910
|
+
return new RafAniTimeline(rootNode, clock);
|
|
800
911
|
}
|
|
801
912
|
|
|
802
|
-
// src/ani/states.ts
|
|
913
|
+
// src/ani/raf/states.ts
|
|
803
914
|
function createStates(config) {
|
|
804
915
|
let State = config.initial;
|
|
805
|
-
let
|
|
916
|
+
let Timeline = rafTimeline(
|
|
806
917
|
config.states[State],
|
|
807
918
|
config.clock
|
|
808
919
|
);
|
|
809
920
|
const subs = /* @__PURE__ */ new Set();
|
|
810
|
-
const notify = (
|
|
921
|
+
const notify = (timeline) => {
|
|
811
922
|
for (const Sub of subs) {
|
|
812
|
-
Sub(
|
|
923
|
+
Sub(timeline);
|
|
813
924
|
}
|
|
814
925
|
};
|
|
815
926
|
return {
|
|
816
|
-
timeline: () =>
|
|
927
|
+
timeline: () => Timeline,
|
|
928
|
+
state: () => State,
|
|
817
929
|
onTimelineChange(callback) {
|
|
818
930
|
subs.add(callback);
|
|
819
931
|
return () => subs.delete(callback);
|
|
820
932
|
},
|
|
821
933
|
transitionTo(newState, timelineConfig, canBeIntercepted) {
|
|
822
|
-
if (!config.states[newState] || State === newState) {
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
934
|
const from = timelineConfig?.from ?? // 1. config
|
|
826
|
-
|
|
935
|
+
Timeline.getCurrentValue() ?? // 2. last value
|
|
827
936
|
config.initialFrom;
|
|
828
937
|
State = newState;
|
|
829
|
-
|
|
830
|
-
notify(
|
|
831
|
-
|
|
938
|
+
Timeline = rafTimeline(config.states[State], config.clock);
|
|
939
|
+
notify(Timeline);
|
|
940
|
+
Timeline.play(
|
|
832
941
|
{
|
|
833
942
|
...timelineConfig,
|
|
834
943
|
from
|
|
@@ -839,77 +948,6 @@ function createStates(config) {
|
|
|
839
948
|
};
|
|
840
949
|
}
|
|
841
950
|
|
|
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
951
|
// src/style/create_sheet.ts
|
|
914
952
|
var TransformFunctionMap = {
|
|
915
953
|
// deg
|
|
@@ -997,40 +1035,285 @@ function createStyleSheet(animeStyleValue, resolver) {
|
|
|
997
1035
|
return styleAccumulator;
|
|
998
1036
|
}
|
|
999
1037
|
|
|
1038
|
+
// src/ani/waapi/compiler/resolver.ts
|
|
1039
|
+
function resolveStateAt(plan, initialFrom, targetTime, dt) {
|
|
1040
|
+
const { keyMap, values: initialValues } = resolveGroup(initialFrom);
|
|
1041
|
+
const rawResultState = resolvePlanState(
|
|
1042
|
+
plan,
|
|
1043
|
+
initialValues,
|
|
1044
|
+
keyMap,
|
|
1045
|
+
targetTime,
|
|
1046
|
+
dt
|
|
1047
|
+
);
|
|
1048
|
+
return resolveStateToGroup(rawResultState, keyMap);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/ani/waapi/compiler/timing_compiler.ts
|
|
1052
|
+
function compileTiming(timing) {
|
|
1053
|
+
if (!timing) {
|
|
1054
|
+
return "linear";
|
|
1055
|
+
}
|
|
1056
|
+
if (timing instanceof LinearTimingFunction) {
|
|
1057
|
+
return "linear";
|
|
1058
|
+
}
|
|
1059
|
+
if (timing instanceof BezierTimingFunction) {
|
|
1060
|
+
const { p2, p3 } = timing.opt;
|
|
1061
|
+
return `cubic-bezier(${p2.x}, ${p2.y}, ${p3.x}, ${p3.y})`;
|
|
1062
|
+
}
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/ani/waapi/compiler/keyframe_compiler.ts
|
|
1067
|
+
function compileToKeyframes(plan, initialFrom) {
|
|
1068
|
+
if (plan.length === 0) {
|
|
1069
|
+
return [];
|
|
1070
|
+
}
|
|
1071
|
+
const FPS = 60;
|
|
1072
|
+
const SAMPLE_RATE = 1 / FPS;
|
|
1073
|
+
const duration = Math.max(...plan.map((s) => s.endTime));
|
|
1074
|
+
if (duration === 0) {
|
|
1075
|
+
const state = resolveStateAt(plan, initialFrom, 0, SAMPLE_RATE);
|
|
1076
|
+
const style = createStyleSheet(state);
|
|
1077
|
+
return [
|
|
1078
|
+
{ offset: 0, ...style },
|
|
1079
|
+
{ offset: 1, ...style }
|
|
1080
|
+
];
|
|
1081
|
+
}
|
|
1082
|
+
const timePoints = /* @__PURE__ */ new Set([0, duration]);
|
|
1083
|
+
for (const seg of plan) {
|
|
1084
|
+
timePoints.add(seg.startTime);
|
|
1085
|
+
timePoints.add(seg.endTime);
|
|
1086
|
+
}
|
|
1087
|
+
const sortedTimes = Array.from(timePoints).sort((a2, b) => a2 - b);
|
|
1088
|
+
const keyframes = [];
|
|
1089
|
+
const getEasingForInterval = (t, nextT) => {
|
|
1090
|
+
const activeSegments = plan.filter(
|
|
1091
|
+
(s) => s.startTime <= t && s.endTime >= nextT
|
|
1092
|
+
);
|
|
1093
|
+
if (activeSegments.length === 0) return "linear";
|
|
1094
|
+
const timings = activeSegments.map((s) => s.node.props.timing).filter((t2) => t2 !== void 0);
|
|
1095
|
+
if (timings.length === 0) return "linear";
|
|
1096
|
+
const firstTiming = timings[0];
|
|
1097
|
+
const allSame = timings.every((t2) => t2 === firstTiming);
|
|
1098
|
+
if (allSame && firstTiming instanceof TimingFunction) {
|
|
1099
|
+
return compileTiming(firstTiming);
|
|
1100
|
+
}
|
|
1101
|
+
return null;
|
|
1102
|
+
};
|
|
1103
|
+
for (let i = 0; i < sortedTimes.length; i++) {
|
|
1104
|
+
const currT = sortedTimes[i];
|
|
1105
|
+
const state = resolveStateAt(plan, initialFrom, currT, SAMPLE_RATE);
|
|
1106
|
+
const style = createStyleSheet(state);
|
|
1107
|
+
const keyframe = {
|
|
1108
|
+
offset: currT / duration,
|
|
1109
|
+
...style
|
|
1110
|
+
};
|
|
1111
|
+
keyframes.push(keyframe);
|
|
1112
|
+
if (i < sortedTimes.length - 1) {
|
|
1113
|
+
const nextT = sortedTimes[i + 1];
|
|
1114
|
+
const easing = getEasingForInterval(currT, nextT);
|
|
1115
|
+
if (easing === null) {
|
|
1116
|
+
let sampleT = currT + SAMPLE_RATE;
|
|
1117
|
+
while (sampleT < nextT) {
|
|
1118
|
+
const sampleState = resolveStateAt(
|
|
1119
|
+
plan,
|
|
1120
|
+
initialFrom,
|
|
1121
|
+
sampleT,
|
|
1122
|
+
SAMPLE_RATE
|
|
1123
|
+
);
|
|
1124
|
+
const sampleStyle = createStyleSheet(
|
|
1125
|
+
sampleState
|
|
1126
|
+
);
|
|
1127
|
+
keyframes.push({
|
|
1128
|
+
offset: sampleT / duration,
|
|
1129
|
+
...sampleStyle,
|
|
1130
|
+
easing: "linear"
|
|
1131
|
+
});
|
|
1132
|
+
sampleT += SAMPLE_RATE;
|
|
1133
|
+
}
|
|
1134
|
+
keyframe.easing = "linear";
|
|
1135
|
+
} else {
|
|
1136
|
+
keyframe.easing = easing;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return keyframes;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// src/ani/waapi/timeline.ts
|
|
1144
|
+
var WebAniTimeline = class extends TimelineBase {
|
|
1145
|
+
constructor(rootNode) {
|
|
1146
|
+
super(rootNode);
|
|
1147
|
+
this._animation = null;
|
|
1148
|
+
this._keyframes = [];
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Plays animation.
|
|
1152
|
+
* @param target Target element.
|
|
1153
|
+
* @param config {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API Web Animations API} based config.
|
|
1154
|
+
*/
|
|
1155
|
+
play(target, config) {
|
|
1156
|
+
if (this._animation) {
|
|
1157
|
+
this._animation.cancel();
|
|
1158
|
+
}
|
|
1159
|
+
this._currentExecutionPlan = this._resolveExecutionPlan(
|
|
1160
|
+
config.keyframes,
|
|
1161
|
+
config.durations
|
|
1162
|
+
);
|
|
1163
|
+
this._keyframes = compileToKeyframes(
|
|
1164
|
+
this._currentExecutionPlan,
|
|
1165
|
+
config.from
|
|
1166
|
+
);
|
|
1167
|
+
if (this._keyframes.length === 0) {
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
const totalDurationMs = this._currentExecutionPlan.reduce(
|
|
1171
|
+
(max, seg) => Math.max(max, seg.endTime),
|
|
1172
|
+
0
|
|
1173
|
+
) * 1e3;
|
|
1174
|
+
const effect = new KeyframeEffect(target, this._keyframes, {
|
|
1175
|
+
duration: totalDurationMs,
|
|
1176
|
+
iterations: config.repeat ?? 1,
|
|
1177
|
+
delay: config.delay ?? 0,
|
|
1178
|
+
fill: "forwards"
|
|
1179
|
+
});
|
|
1180
|
+
this._animation = new Animation(effect, document.timeline);
|
|
1181
|
+
this._animation.play();
|
|
1182
|
+
return this._animation;
|
|
1183
|
+
}
|
|
1184
|
+
pause() {
|
|
1185
|
+
this._animation?.pause();
|
|
1186
|
+
}
|
|
1187
|
+
resume() {
|
|
1188
|
+
this._animation?.play();
|
|
1189
|
+
}
|
|
1190
|
+
reset() {
|
|
1191
|
+
this._animation?.cancel();
|
|
1192
|
+
this._animation = null;
|
|
1193
|
+
}
|
|
1194
|
+
seek(targetTime) {
|
|
1195
|
+
if (this._animation) {
|
|
1196
|
+
this._animation.currentTime = targetTime * 1e3;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Native animation object.
|
|
1201
|
+
*
|
|
1202
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Animation Animation}.
|
|
1203
|
+
*/
|
|
1204
|
+
get nativeAnimation() {
|
|
1205
|
+
return this._animation;
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
function webTimeline(rootNode) {
|
|
1209
|
+
return new WebAniTimeline(rootNode);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// src/event/manager.ts
|
|
1213
|
+
var EventManager = class _EventManager {
|
|
1214
|
+
constructor(supportedEvents) {
|
|
1215
|
+
this.supportedEvents = supportedEvents;
|
|
1216
|
+
this._element = null;
|
|
1217
|
+
this._animeGetter = null;
|
|
1218
|
+
this.setAnimeGetter = (animeGetter) => {
|
|
1219
|
+
this._animeGetter = animeGetter;
|
|
1220
|
+
};
|
|
1221
|
+
this.eventMap = /* @__PURE__ */ new Map();
|
|
1222
|
+
this.withAnimeValue = (listener) => {
|
|
1223
|
+
const withAnime = (e) => {
|
|
1224
|
+
listener(this.animeGetter(), e);
|
|
1225
|
+
};
|
|
1226
|
+
return withAnime;
|
|
1227
|
+
};
|
|
1228
|
+
this.add = (eventName, listener, options) => {
|
|
1229
|
+
const withAnime = this.withAnimeValue(listener);
|
|
1230
|
+
this.eventMap.set(eventName, withAnime);
|
|
1231
|
+
this.targetElement.addEventListener(
|
|
1232
|
+
eventName,
|
|
1233
|
+
this.eventMap.get(eventName),
|
|
1234
|
+
options
|
|
1235
|
+
);
|
|
1236
|
+
};
|
|
1237
|
+
this.cleanupOne = (eventName) => {
|
|
1238
|
+
const removeListener = this.eventMap.get(eventName);
|
|
1239
|
+
if (!removeListener) return false;
|
|
1240
|
+
this.targetElement.removeEventListener(eventName, removeListener);
|
|
1241
|
+
return true;
|
|
1242
|
+
};
|
|
1243
|
+
this.cleanupAll = () => {
|
|
1244
|
+
const clearResponse = [];
|
|
1245
|
+
for (const evtName of this.eventMap.keys()) {
|
|
1246
|
+
const res = this.cleanupOne(evtName);
|
|
1247
|
+
clearResponse.push(res);
|
|
1248
|
+
}
|
|
1249
|
+
return clearResponse.some((t) => t === false) === false;
|
|
1250
|
+
};
|
|
1251
|
+
this.attach = (handlers) => {
|
|
1252
|
+
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
1253
|
+
this.add(
|
|
1254
|
+
_EventManager.getEvtKey(eventKey),
|
|
1255
|
+
handler
|
|
1256
|
+
);
|
|
1257
|
+
});
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
get targetElement() {
|
|
1261
|
+
if (!this._element) throw new Error("EventManger, bind element first");
|
|
1262
|
+
return this._element;
|
|
1263
|
+
}
|
|
1264
|
+
get animeGetter() {
|
|
1265
|
+
if (!this._animeGetter)
|
|
1266
|
+
throw new Error("EventManager, animeGetter should be provided");
|
|
1267
|
+
return this._animeGetter;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* get pure `{event_name}`
|
|
1271
|
+
* @param key onX`{event_name}`
|
|
1272
|
+
*/
|
|
1273
|
+
static getEvtKey(key) {
|
|
1274
|
+
const removed = key.substring(2, key.length);
|
|
1275
|
+
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
1276
|
+
return Capitalized;
|
|
1277
|
+
}
|
|
1278
|
+
bind(element) {
|
|
1279
|
+
this._element = element;
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1000
1283
|
// src/index.ts
|
|
1001
1284
|
var a = {
|
|
1002
1285
|
timing: T,
|
|
1286
|
+
dynamicTimeline: rafTimeline,
|
|
1287
|
+
timeline: webTimeline,
|
|
1288
|
+
/**
|
|
1289
|
+
* Create animation segment.
|
|
1290
|
+
*/
|
|
1003
1291
|
ani,
|
|
1004
|
-
|
|
1292
|
+
/**
|
|
1293
|
+
* Add delay
|
|
1294
|
+
*/
|
|
1005
1295
|
delay,
|
|
1006
1296
|
loop,
|
|
1007
1297
|
parallel,
|
|
1008
1298
|
sequence,
|
|
1009
1299
|
stagger,
|
|
1010
|
-
|
|
1300
|
+
createStates
|
|
1011
1301
|
};
|
|
1012
1302
|
export {
|
|
1013
1303
|
AnimationClock,
|
|
1014
|
-
|
|
1015
|
-
CompositionNode,
|
|
1304
|
+
BezierTimingFunction,
|
|
1016
1305
|
EventManager,
|
|
1017
1306
|
LinearTimingFunction,
|
|
1018
|
-
|
|
1019
|
-
SegmentNode,
|
|
1020
|
-
SequenceNode,
|
|
1021
|
-
StaggerNode,
|
|
1307
|
+
RafAniTimeline,
|
|
1022
1308
|
T,
|
|
1023
|
-
|
|
1309
|
+
TimelineBase,
|
|
1024
1310
|
TimingFunction,
|
|
1311
|
+
WebAniTimeline,
|
|
1025
1312
|
a,
|
|
1026
|
-
|
|
1313
|
+
calculateSegmentState,
|
|
1314
|
+
compileToKeyframes,
|
|
1027
1315
|
createStates,
|
|
1028
1316
|
createStyleSheet,
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
parallel,
|
|
1032
|
-
sequence,
|
|
1033
|
-
stagger,
|
|
1034
|
-
timeline
|
|
1317
|
+
rafTimeline,
|
|
1318
|
+
webTimeline
|
|
1035
1319
|
};
|
|
1036
|
-
//# sourceMappingURL=index.js.map
|