@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.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,42 +104,122 @@ var TimingFunction = class _TimingFunction {
|
|
|
75
104
|
}
|
|
76
105
|
};
|
|
77
106
|
|
|
78
|
-
// src/timing/linear.ts
|
|
79
|
-
var LinearTimingFunction = class extends TimingFunction {
|
|
80
|
-
step(time, context) {
|
|
81
|
-
const progress = context.duration === 0 ? 1 : Math.max(0, Math.min(time / context.duration, 1));
|
|
82
|
-
const value = context.from + (context.to - context.from) * progress;
|
|
83
|
-
return { value, endOfAnimation: time >= context.duration };
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
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);
|
|
88
114
|
var BezierTimingFunction = class extends TimingFunction {
|
|
89
115
|
constructor(opt) {
|
|
90
116
|
super();
|
|
91
117
|
this.opt = opt;
|
|
92
|
-
this.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
}
|
|
100
129
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|
|
104
191
|
}
|
|
105
192
|
step(time, context) {
|
|
106
|
-
const
|
|
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;
|
|
107
207
|
return {
|
|
108
|
-
value
|
|
109
|
-
endOfAnimation
|
|
208
|
+
value,
|
|
209
|
+
endOfAnimation
|
|
110
210
|
};
|
|
111
211
|
}
|
|
112
212
|
};
|
|
113
213
|
|
|
214
|
+
// src/timing/linear.ts
|
|
215
|
+
var LinearTimingFunction = class extends TimingFunction {
|
|
216
|
+
step(time, context) {
|
|
217
|
+
const progress = context.duration === 0 ? 1 : Math.max(0, Math.min(time / context.duration, 1));
|
|
218
|
+
const value = context.from + (context.to - context.from) * progress;
|
|
219
|
+
return { value, endOfAnimation: time >= context.duration };
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
114
223
|
// src/timing/dynamic_spring.ts
|
|
115
224
|
var DynamicSpringTimingFunction = class extends TimingFunction {
|
|
116
225
|
constructor(opt) {
|
|
@@ -268,10 +377,38 @@ var T = {
|
|
|
268
377
|
/**
|
|
269
378
|
* Creates linear timing function instance.
|
|
270
379
|
*/
|
|
271
|
-
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
|
+
})
|
|
272
409
|
};
|
|
273
410
|
|
|
274
|
-
// src/
|
|
411
|
+
// src/nodes/segment.ts
|
|
275
412
|
var SegmentNode = class extends AnimationNode {
|
|
276
413
|
constructor(props, id) {
|
|
277
414
|
super(id);
|
|
@@ -298,7 +435,7 @@ function ani(props, id) {
|
|
|
298
435
|
return new SegmentNode(props, id);
|
|
299
436
|
}
|
|
300
437
|
|
|
301
|
-
// src/
|
|
438
|
+
// src/nodes/composition.ts
|
|
302
439
|
var CompositionNode = class _CompositionNode extends AnimationNode {
|
|
303
440
|
constructor(children, timing, id) {
|
|
304
441
|
super(id);
|
|
@@ -327,12 +464,12 @@ var CompositionNode = class _CompositionNode extends AnimationNode {
|
|
|
327
464
|
}
|
|
328
465
|
};
|
|
329
466
|
|
|
330
|
-
// src/
|
|
467
|
+
// src/nodes/delay.ts
|
|
331
468
|
function delay(duration, id) {
|
|
332
469
|
return new SegmentNode({ to: {}, duration }, id);
|
|
333
470
|
}
|
|
334
471
|
|
|
335
|
-
// src/
|
|
472
|
+
// src/nodes/loop.ts
|
|
336
473
|
var LoopNode = class extends CompositionNode {
|
|
337
474
|
constructor(child, loopCount, timing, id) {
|
|
338
475
|
super([child], timing, id);
|
|
@@ -353,7 +490,7 @@ function loop(child, loopCount, timing, id) {
|
|
|
353
490
|
return new LoopNode(child, loopCount, timing, id);
|
|
354
491
|
}
|
|
355
492
|
|
|
356
|
-
// src/
|
|
493
|
+
// src/nodes/parallel.ts
|
|
357
494
|
var ParallelNode = class extends CompositionNode {
|
|
358
495
|
constructor(children, timing, id) {
|
|
359
496
|
const seenProperty = /* @__PURE__ */ new Set();
|
|
@@ -403,7 +540,7 @@ function parallel(children, timing, id) {
|
|
|
403
540
|
return new ParallelNode(children, timing, id);
|
|
404
541
|
}
|
|
405
542
|
|
|
406
|
-
// src/
|
|
543
|
+
// src/nodes/sequence.ts
|
|
407
544
|
var SequenceNode = class extends CompositionNode {
|
|
408
545
|
constructor(children, timing, id) {
|
|
409
546
|
super(children, timing, id);
|
|
@@ -422,12 +559,12 @@ function sequence(children, timing, id) {
|
|
|
422
559
|
return new SequenceNode(children, timing, id);
|
|
423
560
|
}
|
|
424
561
|
|
|
425
|
-
// src/
|
|
562
|
+
// src/nodes/stagger.ts
|
|
426
563
|
var StaggerNode = class extends CompositionNode {
|
|
427
|
-
constructor(children,
|
|
428
|
-
super(children,
|
|
564
|
+
constructor(children, offset, timing, id) {
|
|
565
|
+
super(children, timing, id);
|
|
429
566
|
this.type = "STAGGER";
|
|
430
|
-
this.offset =
|
|
567
|
+
this.offset = offset;
|
|
431
568
|
if (children.length === 0) {
|
|
432
569
|
this.duration = 0;
|
|
433
570
|
} else {
|
|
@@ -443,10 +580,81 @@ var StaggerNode = class extends CompositionNode {
|
|
|
443
580
|
}
|
|
444
581
|
}
|
|
445
582
|
};
|
|
446
|
-
function stagger(children,
|
|
447
|
-
return new StaggerNode(children,
|
|
583
|
+
function stagger(children, offset, timing, id) {
|
|
584
|
+
return new StaggerNode(children, offset, timing, id);
|
|
448
585
|
}
|
|
449
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
|
+
|
|
450
658
|
// src/loop/clock.ts
|
|
451
659
|
var AnimationClock = class _AnimationClock {
|
|
452
660
|
constructor(maxDeltaTime) {
|
|
@@ -502,46 +710,86 @@ var AnimationClock = class _AnimationClock {
|
|
|
502
710
|
}
|
|
503
711
|
};
|
|
504
712
|
|
|
505
|
-
// src/
|
|
506
|
-
function
|
|
507
|
-
|
|
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 };
|
|
508
723
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const t = Math.max(0, Math.min(localTime, segmentDef.duration));
|
|
513
|
-
const animeValues = [];
|
|
514
|
-
let allComplete = true;
|
|
515
|
-
const isMultipleTiming = Array.isArray(segmentDef.timing);
|
|
516
|
-
if (isMultipleTiming && segmentDef.timing.length !== segmentDef.from.length) {
|
|
517
|
-
throw new TypeError(
|
|
518
|
-
`[calculateSegmentState] timing does not correctly set. It requires multiple timing for ${segmentDef.from}, but received ${segmentDef.timing}`
|
|
519
|
-
);
|
|
724
|
+
function resolveStateToGroup(state, keyMap) {
|
|
725
|
+
if (!keyMap) {
|
|
726
|
+
return state;
|
|
520
727
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
+
}
|
|
532
784
|
}
|
|
533
785
|
}
|
|
534
|
-
return
|
|
535
|
-
values: animeValues,
|
|
536
|
-
isComplete: allComplete || isEndOfAnimation(t, segmentDef.duration)
|
|
537
|
-
};
|
|
786
|
+
return nextState;
|
|
538
787
|
}
|
|
539
788
|
|
|
540
|
-
// src/ani/timeline.ts
|
|
541
|
-
var
|
|
789
|
+
// src/ani/raf/timeline.ts
|
|
790
|
+
var RafAniTimeline = class extends TimelineBase {
|
|
542
791
|
constructor(rootNode, clock) {
|
|
543
|
-
|
|
544
|
-
this._currentExecutionPlan = null;
|
|
792
|
+
super(rootNode);
|
|
545
793
|
this._masterTime = 0;
|
|
546
794
|
this._delay = 0;
|
|
547
795
|
this._status = "IDLE";
|
|
@@ -550,194 +798,86 @@ var Timeline = class {
|
|
|
550
798
|
this._initialState = [];
|
|
551
799
|
this._repeatCount = 0;
|
|
552
800
|
this._propertyKeyMap = null;
|
|
553
|
-
this._segmentStartStates = /* @__PURE__ */ new Map();
|
|
554
801
|
this._onUpdateCallbacks = /* @__PURE__ */ new Set();
|
|
555
|
-
this.duration = rootNode.duration;
|
|
556
|
-
this._baseExecutionPlan = this._constructExecutionPlan(rootNode);
|
|
557
802
|
this._clock = clock ?? AnimationClock.create();
|
|
558
|
-
this.play.bind(this);
|
|
559
|
-
this.pause.bind(this);
|
|
560
|
-
this.seek.bind(this);
|
|
561
|
-
this.resume.bind(this);
|
|
562
|
-
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);
|
|
563
808
|
}
|
|
564
|
-
/**
|
|
565
|
-
* Current animation running config.
|
|
566
|
-
*/
|
|
567
809
|
get currentConfig() {
|
|
568
810
|
return this._currentConfig;
|
|
569
811
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
577
|
-
const keyMap = new Map(Object.keys(group).map((key, i) => [key, i]));
|
|
578
|
-
const values = Object.values(group);
|
|
579
|
-
return { keyMap, values };
|
|
812
|
+
getCurrentValue() {
|
|
813
|
+
if (this._state.length === 0) return null;
|
|
814
|
+
return resolveStateToGroup(
|
|
815
|
+
this._state,
|
|
816
|
+
this._propertyKeyMap
|
|
817
|
+
);
|
|
580
818
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
_resolveStateToGroup(state) {
|
|
585
|
-
if (!this._propertyKeyMap) {
|
|
586
|
-
return state;
|
|
587
|
-
}
|
|
588
|
-
const group = {};
|
|
589
|
-
let i = 0;
|
|
590
|
-
for (const key of this._propertyKeyMap.keys()) {
|
|
591
|
-
group[key] = state[i];
|
|
592
|
-
i++;
|
|
819
|
+
_calculateStateAtTime(targetTime, dt = 0) {
|
|
820
|
+
if (this._initialState.length === 0 || !this._currentExecutionPlan) {
|
|
821
|
+
return [];
|
|
593
822
|
}
|
|
594
|
-
return
|
|
823
|
+
return resolvePlanState(
|
|
824
|
+
this._currentExecutionPlan,
|
|
825
|
+
this._initialState,
|
|
826
|
+
this._propertyKeyMap,
|
|
827
|
+
// Using the class property directly
|
|
828
|
+
targetTime,
|
|
829
|
+
dt
|
|
830
|
+
);
|
|
595
831
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
+
}
|
|
603
842
|
}
|
|
604
843
|
/**
|
|
605
|
-
*
|
|
844
|
+
* @private Internal clock subscription callback.
|
|
606
845
|
*/
|
|
607
|
-
|
|
608
|
-
if (this.
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (targetTime < segment.startTime) {
|
|
615
|
-
continue;
|
|
616
|
-
}
|
|
617
|
-
if (!segment.node.props.timing) {
|
|
618
|
-
throw new Error(
|
|
619
|
-
`[Timeline] timing should be provided. Please specify timing using a.timing.(...). Check target segment: ${JSON.stringify(segment, null, 2)}.`,
|
|
620
|
-
{ cause: segment }
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
stateAtLastStartTime = [...nextState];
|
|
624
|
-
const { keyMap, values: toValues } = this._resolveGroup(
|
|
625
|
-
segment.node.props.to
|
|
626
|
-
);
|
|
627
|
-
const isRecordAni = this._propertyKeyMap !== null && keyMap !== null;
|
|
628
|
-
let fromValues = [];
|
|
629
|
-
const timings = [];
|
|
630
|
-
const t = segment.node.props.timing;
|
|
631
|
-
const isRecordTiming = t && !(t instanceof TimingFunction);
|
|
632
|
-
if (isRecordAni) {
|
|
633
|
-
for (const key of keyMap.keys()) {
|
|
634
|
-
const index = this._propertyKeyMap.get(key);
|
|
635
|
-
fromValues.push(stateAtLastStartTime[index]);
|
|
636
|
-
if (isRecordTiming) {
|
|
637
|
-
timings.push(
|
|
638
|
-
t[key]
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
} else {
|
|
643
|
-
fromValues = stateAtLastStartTime;
|
|
644
|
-
}
|
|
645
|
-
let finalAnimeValues = [];
|
|
646
|
-
const localTime = targetTime - segment.startTime;
|
|
647
|
-
const segmentDef = {
|
|
648
|
-
from: fromValues,
|
|
649
|
-
to: toValues,
|
|
650
|
-
duration: segment.node.duration,
|
|
651
|
-
timing: isRecordAni && isRecordTiming ? timings : t
|
|
652
|
-
};
|
|
653
|
-
const segmentState = calculateSegmentState(
|
|
654
|
-
localTime,
|
|
655
|
-
segmentDef,
|
|
656
|
-
dt
|
|
657
|
-
);
|
|
658
|
-
if (segmentState.isComplete) {
|
|
659
|
-
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;
|
|
660
853
|
} else {
|
|
661
|
-
|
|
854
|
+
return;
|
|
662
855
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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();
|
|
673
868
|
} else {
|
|
674
|
-
|
|
675
|
-
nextState[i] = finalAnimeValues[i];
|
|
676
|
-
}
|
|
869
|
+
this.play(this._currentConfig);
|
|
677
870
|
}
|
|
678
871
|
}
|
|
679
|
-
return nextState;
|
|
680
|
-
}
|
|
681
|
-
_resolveExecutionPlan(keyframes, durations) {
|
|
682
|
-
if (!keyframes && !durations) {
|
|
683
|
-
return [...this._baseExecutionPlan];
|
|
684
|
-
}
|
|
685
|
-
const segmentNodes = this._baseExecutionPlan.filter(
|
|
686
|
-
(segment) => segment.node.type === "SEGMENT"
|
|
687
|
-
);
|
|
688
|
-
const segLength = segmentNodes.length;
|
|
689
|
-
if (keyframes && keyframes.length !== segLength) {
|
|
690
|
-
throw new Error(
|
|
691
|
-
`Timeline keyframe mismatch: Expected ${segLength} keyframes, but received ${keyframes.length}.`
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
if (durations && durations.length !== segLength) {
|
|
695
|
-
throw new Error(
|
|
696
|
-
`Timeline keyframe mismatch: Expected ${segLength} durations, but received ${durations.length}.`
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
const newPlan = [];
|
|
700
|
-
let keyframeIndex = 0;
|
|
701
|
-
for (const segment of this._baseExecutionPlan) {
|
|
702
|
-
if (segment.node.type === "SEGMENT") {
|
|
703
|
-
const dynamicTo = keyframes?.[keyframeIndex];
|
|
704
|
-
const dynamicDuration = durations?.[keyframeIndex];
|
|
705
|
-
const newSegmentProps = {
|
|
706
|
-
...segment.node.props,
|
|
707
|
-
// >> dynamic to
|
|
708
|
-
...dynamicTo && {
|
|
709
|
-
to: dynamicTo === "keep" ? segment.node.props.to : dynamicTo
|
|
710
|
-
},
|
|
711
|
-
// >> dynamic duration
|
|
712
|
-
...dynamicDuration && {
|
|
713
|
-
duration: dynamicDuration === "keep" ? segment.node.props.duration : dynamicDuration
|
|
714
|
-
}
|
|
715
|
-
};
|
|
716
|
-
const newSegment = new SegmentNode(
|
|
717
|
-
newSegmentProps,
|
|
718
|
-
segment.node.id
|
|
719
|
-
);
|
|
720
|
-
newPlan.push({ ...segment, node: newSegment });
|
|
721
|
-
keyframeIndex++;
|
|
722
|
-
} else {
|
|
723
|
-
newPlan.push({ ...segment });
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
return newPlan;
|
|
727
|
-
}
|
|
728
|
-
notify() {
|
|
729
|
-
for (const subscriber of this._onUpdateCallbacks) {
|
|
730
|
-
subscriber({
|
|
731
|
-
state: this._resolveStateToGroup(this._state),
|
|
732
|
-
status: this._status
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
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
|
+
*/
|
|
736
878
|
play(config, canBeIntercepted = true) {
|
|
737
|
-
if (this._status === "PLAYING" && !canBeIntercepted)
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
const isRepeating = this._currentConfig?.repeat && this._currentConfig?.repeat >= 1;
|
|
879
|
+
if (this._status === "PLAYING" && !canBeIntercepted) return;
|
|
880
|
+
const isRepeating = (this._currentConfig?.repeat ?? 0) >= 1;
|
|
741
881
|
const savedRepeatCount = isRepeating ? this._repeatCount : 0;
|
|
742
882
|
this.reset(false);
|
|
743
883
|
this._repeatCount = savedRepeatCount;
|
|
@@ -753,8 +893,8 @@ var Timeline = class {
|
|
|
753
893
|
config.keyframes,
|
|
754
894
|
config.durations
|
|
755
895
|
);
|
|
756
|
-
const { keyMap
|
|
757
|
-
this._propertyKeyMap =
|
|
896
|
+
const { keyMap, values } = resolveGroup(config.from);
|
|
897
|
+
this._propertyKeyMap = keyMap;
|
|
758
898
|
this._state = values;
|
|
759
899
|
this._initialState = values;
|
|
760
900
|
this._status = "PLAYING";
|
|
@@ -778,13 +918,10 @@ var Timeline = class {
|
|
|
778
918
|
this._state = [];
|
|
779
919
|
this._initialState = [];
|
|
780
920
|
this._propertyKeyMap = null;
|
|
781
|
-
this._segmentStartStates.clear();
|
|
782
921
|
this._currentExecutionPlan = null;
|
|
783
922
|
this._clock.unsubscribe(this);
|
|
784
923
|
this._repeatCount = 0;
|
|
785
|
-
if (notify)
|
|
786
|
-
this.notify();
|
|
787
|
-
}
|
|
924
|
+
if (notify) this.notify();
|
|
788
925
|
}
|
|
789
926
|
seek(targetTime) {
|
|
790
927
|
if (this._status === "PLAYING" || this._status === "ENDED") {
|
|
@@ -795,87 +932,50 @@ var Timeline = class {
|
|
|
795
932
|
this._state = this._calculateStateAtTime(seekTime, 0);
|
|
796
933
|
this.notify();
|
|
797
934
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
935
|
+
/**
|
|
936
|
+
* When timeline updates, subscribes on update callback.
|
|
937
|
+
* @param callback Subscription callback.
|
|
938
|
+
* @returns Unsubscribe.
|
|
939
|
+
*/
|
|
802
940
|
onUpdate(callback) {
|
|
803
941
|
this._onUpdateCallbacks.add(callback);
|
|
804
942
|
return () => {
|
|
805
943
|
this._onUpdateCallbacks.delete(callback);
|
|
806
944
|
};
|
|
807
945
|
}
|
|
808
|
-
update(dt) {
|
|
809
|
-
if (this._status !== "PLAYING") {
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
if (this._delay > 0) {
|
|
813
|
-
this._delay -= dt;
|
|
814
|
-
if (this._delay < 0) {
|
|
815
|
-
dt = -this._delay;
|
|
816
|
-
this._delay = 0;
|
|
817
|
-
} else {
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
this._masterTime += dt;
|
|
822
|
-
if (this._masterTime >= this.duration) {
|
|
823
|
-
this._masterTime = this.duration;
|
|
824
|
-
}
|
|
825
|
-
this._state = this._calculateStateAtTime(this._masterTime, dt);
|
|
826
|
-
this.notify();
|
|
827
|
-
if (isEndOfAnimation(this._masterTime, this.duration)) {
|
|
828
|
-
this._repeatCount += 1;
|
|
829
|
-
if (!this._currentConfig) {
|
|
830
|
-
throw new Error(
|
|
831
|
-
`[Timeline] currentConfig can not be null when update(dt)`
|
|
832
|
-
);
|
|
833
|
-
}
|
|
834
|
-
const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
|
|
835
|
-
if (noRepeat) {
|
|
836
|
-
this._status = "ENDED";
|
|
837
|
-
this._clock.unsubscribe(this);
|
|
838
|
-
this.notify();
|
|
839
|
-
} else {
|
|
840
|
-
this.play(this._currentConfig);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
946
|
};
|
|
845
|
-
function
|
|
846
|
-
return new
|
|
947
|
+
function rafTimeline(rootNode, clock) {
|
|
948
|
+
return new RafAniTimeline(rootNode, clock);
|
|
847
949
|
}
|
|
848
950
|
|
|
849
|
-
// src/ani/states.ts
|
|
951
|
+
// src/ani/raf/states.ts
|
|
850
952
|
function createStates(config) {
|
|
851
953
|
let State = config.initial;
|
|
852
|
-
let
|
|
954
|
+
let Timeline = rafTimeline(
|
|
853
955
|
config.states[State],
|
|
854
956
|
config.clock
|
|
855
957
|
);
|
|
856
958
|
const subs = /* @__PURE__ */ new Set();
|
|
857
|
-
const notify = (
|
|
959
|
+
const notify = (timeline) => {
|
|
858
960
|
for (const Sub of subs) {
|
|
859
|
-
Sub(
|
|
961
|
+
Sub(timeline);
|
|
860
962
|
}
|
|
861
963
|
};
|
|
862
964
|
return {
|
|
863
|
-
timeline: () =>
|
|
965
|
+
timeline: () => Timeline,
|
|
966
|
+
state: () => State,
|
|
864
967
|
onTimelineChange(callback) {
|
|
865
968
|
subs.add(callback);
|
|
866
969
|
return () => subs.delete(callback);
|
|
867
970
|
},
|
|
868
971
|
transitionTo(newState, timelineConfig, canBeIntercepted) {
|
|
869
|
-
if (!config.states[newState] || State === newState) {
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
972
|
const from = timelineConfig?.from ?? // 1. config
|
|
873
|
-
|
|
973
|
+
Timeline.getCurrentValue() ?? // 2. last value
|
|
874
974
|
config.initialFrom;
|
|
875
975
|
State = newState;
|
|
876
|
-
|
|
877
|
-
notify(
|
|
878
|
-
|
|
976
|
+
Timeline = rafTimeline(config.states[State], config.clock);
|
|
977
|
+
notify(Timeline);
|
|
978
|
+
Timeline.play(
|
|
879
979
|
{
|
|
880
980
|
...timelineConfig,
|
|
881
981
|
from
|
|
@@ -886,77 +986,6 @@ function createStates(config) {
|
|
|
886
986
|
};
|
|
887
987
|
}
|
|
888
988
|
|
|
889
|
-
// src/event/manager.ts
|
|
890
|
-
var EventManager = class _EventManager {
|
|
891
|
-
constructor(supportedEvents) {
|
|
892
|
-
this.supportedEvents = supportedEvents;
|
|
893
|
-
this._element = null;
|
|
894
|
-
this._animeGetter = null;
|
|
895
|
-
this.setAnimeGetter = (animeGetter) => {
|
|
896
|
-
this._animeGetter = animeGetter;
|
|
897
|
-
};
|
|
898
|
-
this.eventMap = /* @__PURE__ */ new Map();
|
|
899
|
-
this.withAnimeValue = (listener) => {
|
|
900
|
-
const withAnime = (e) => {
|
|
901
|
-
listener(this.animeGetter(), e);
|
|
902
|
-
};
|
|
903
|
-
return withAnime;
|
|
904
|
-
};
|
|
905
|
-
this.add = (eventName, listener, options) => {
|
|
906
|
-
const withAnime = this.withAnimeValue(listener);
|
|
907
|
-
this.eventMap.set(eventName, withAnime);
|
|
908
|
-
this.targetElement.addEventListener(
|
|
909
|
-
eventName,
|
|
910
|
-
this.eventMap.get(eventName),
|
|
911
|
-
options
|
|
912
|
-
);
|
|
913
|
-
};
|
|
914
|
-
this.cleanupOne = (eventName) => {
|
|
915
|
-
const removeListener = this.eventMap.get(eventName);
|
|
916
|
-
if (!removeListener) return false;
|
|
917
|
-
this.targetElement.removeEventListener(eventName, removeListener);
|
|
918
|
-
return true;
|
|
919
|
-
};
|
|
920
|
-
this.cleanupAll = () => {
|
|
921
|
-
const clearResponse = [];
|
|
922
|
-
for (const evtName of this.eventMap.keys()) {
|
|
923
|
-
const res = this.cleanupOne(evtName);
|
|
924
|
-
clearResponse.push(res);
|
|
925
|
-
}
|
|
926
|
-
return clearResponse.some((t) => t === false) === false;
|
|
927
|
-
};
|
|
928
|
-
this.attach = (handlers) => {
|
|
929
|
-
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
930
|
-
this.add(
|
|
931
|
-
_EventManager.getEvtKey(eventKey),
|
|
932
|
-
handler
|
|
933
|
-
);
|
|
934
|
-
});
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
get targetElement() {
|
|
938
|
-
if (!this._element) throw new Error("EventManger, bind element first");
|
|
939
|
-
return this._element;
|
|
940
|
-
}
|
|
941
|
-
get animeGetter() {
|
|
942
|
-
if (!this._animeGetter)
|
|
943
|
-
throw new Error("EventManager, animeGetter should be provided");
|
|
944
|
-
return this._animeGetter;
|
|
945
|
-
}
|
|
946
|
-
/**
|
|
947
|
-
* get pure `{event_name}`
|
|
948
|
-
* @param key onX`{event_name}`
|
|
949
|
-
*/
|
|
950
|
-
static getEvtKey(key) {
|
|
951
|
-
const removed = key.substring(2, key.length);
|
|
952
|
-
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
953
|
-
return Capitalized;
|
|
954
|
-
}
|
|
955
|
-
bind(element) {
|
|
956
|
-
this._element = element;
|
|
957
|
-
}
|
|
958
|
-
};
|
|
959
|
-
|
|
960
989
|
// src/style/create_sheet.ts
|
|
961
990
|
var TransformFunctionMap = {
|
|
962
991
|
// deg
|
|
@@ -1044,41 +1073,286 @@ function createStyleSheet(animeStyleValue, resolver) {
|
|
|
1044
1073
|
return styleAccumulator;
|
|
1045
1074
|
}
|
|
1046
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
|
+
|
|
1047
1321
|
// src/index.ts
|
|
1048
1322
|
var a = {
|
|
1049
1323
|
timing: T,
|
|
1324
|
+
dynamicTimeline: rafTimeline,
|
|
1325
|
+
timeline: webTimeline,
|
|
1326
|
+
/**
|
|
1327
|
+
* Create animation segment.
|
|
1328
|
+
*/
|
|
1050
1329
|
ani,
|
|
1051
|
-
|
|
1330
|
+
/**
|
|
1331
|
+
* Add delay
|
|
1332
|
+
*/
|
|
1052
1333
|
delay,
|
|
1053
1334
|
loop,
|
|
1054
1335
|
parallel,
|
|
1055
1336
|
sequence,
|
|
1056
1337
|
stagger,
|
|
1057
|
-
|
|
1338
|
+
createStates
|
|
1058
1339
|
};
|
|
1059
1340
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1060
1341
|
0 && (module.exports = {
|
|
1061
1342
|
AnimationClock,
|
|
1062
|
-
|
|
1063
|
-
CompositionNode,
|
|
1343
|
+
BezierTimingFunction,
|
|
1064
1344
|
EventManager,
|
|
1065
1345
|
LinearTimingFunction,
|
|
1066
|
-
|
|
1067
|
-
SegmentNode,
|
|
1068
|
-
SequenceNode,
|
|
1069
|
-
StaggerNode,
|
|
1346
|
+
RafAniTimeline,
|
|
1070
1347
|
T,
|
|
1071
|
-
|
|
1348
|
+
TimelineBase,
|
|
1072
1349
|
TimingFunction,
|
|
1350
|
+
WebAniTimeline,
|
|
1073
1351
|
a,
|
|
1074
|
-
|
|
1352
|
+
calculateSegmentState,
|
|
1353
|
+
compileToKeyframes,
|
|
1075
1354
|
createStates,
|
|
1076
1355
|
createStyleSheet,
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
parallel,
|
|
1080
|
-
sequence,
|
|
1081
|
-
stagger,
|
|
1082
|
-
timeline
|
|
1356
|
+
rafTimeline,
|
|
1357
|
+
webTimeline
|
|
1083
1358
|
});
|
|
1084
|
-
//# sourceMappingURL=index.cjs.map
|