@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.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,196 +798,89 @@ 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
|
-
this.
|
|
882
|
+
const isPlaying = this._status === "PLAYING";
|
|
883
|
+
this.reset(false, !isPlaying);
|
|
743
884
|
this._repeatCount = savedRepeatCount;
|
|
744
885
|
if (isRepeating && this._repeatCount >= config.repeat) {
|
|
745
886
|
this._repeatCount = 0;
|
|
@@ -753,8 +894,8 @@ var Timeline = class {
|
|
|
753
894
|
config.keyframes,
|
|
754
895
|
config.durations
|
|
755
896
|
);
|
|
756
|
-
const { keyMap
|
|
757
|
-
this._propertyKeyMap =
|
|
897
|
+
const { keyMap, values } = resolveGroup(config.from);
|
|
898
|
+
this._propertyKeyMap = keyMap;
|
|
758
899
|
this._state = values;
|
|
759
900
|
this._initialState = values;
|
|
760
901
|
this._status = "PLAYING";
|
|
@@ -770,7 +911,7 @@ var Timeline = class {
|
|
|
770
911
|
this._status = "PLAYING";
|
|
771
912
|
this._clock.subscribe(this);
|
|
772
913
|
}
|
|
773
|
-
reset(notify = true) {
|
|
914
|
+
reset(notify = true, unsubscribeClock = true) {
|
|
774
915
|
this._status = "IDLE";
|
|
775
916
|
this._currentConfig = null;
|
|
776
917
|
this._masterTime = 0;
|
|
@@ -778,13 +919,12 @@ var Timeline = class {
|
|
|
778
919
|
this._state = [];
|
|
779
920
|
this._initialState = [];
|
|
780
921
|
this._propertyKeyMap = null;
|
|
781
|
-
this._segmentStartStates.clear();
|
|
782
922
|
this._currentExecutionPlan = null;
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (notify) {
|
|
786
|
-
this.notify();
|
|
923
|
+
if (unsubscribeClock) {
|
|
924
|
+
this._clock.unsubscribe(this);
|
|
787
925
|
}
|
|
926
|
+
this._repeatCount = 0;
|
|
927
|
+
if (notify) this.notify();
|
|
788
928
|
}
|
|
789
929
|
seek(targetTime) {
|
|
790
930
|
if (this._status === "PLAYING" || this._status === "ENDED") {
|
|
@@ -795,87 +935,50 @@ var Timeline = class {
|
|
|
795
935
|
this._state = this._calculateStateAtTime(seekTime, 0);
|
|
796
936
|
this.notify();
|
|
797
937
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
938
|
+
/**
|
|
939
|
+
* When timeline updates, subscribes on update callback.
|
|
940
|
+
* @param callback Subscription callback.
|
|
941
|
+
* @returns Unsubscribe.
|
|
942
|
+
*/
|
|
802
943
|
onUpdate(callback) {
|
|
803
944
|
this._onUpdateCallbacks.add(callback);
|
|
804
945
|
return () => {
|
|
805
946
|
this._onUpdateCallbacks.delete(callback);
|
|
806
947
|
};
|
|
807
948
|
}
|
|
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
949
|
};
|
|
845
|
-
function
|
|
846
|
-
return new
|
|
950
|
+
function rafTimeline(rootNode, clock) {
|
|
951
|
+
return new RafAniTimeline(rootNode, clock);
|
|
847
952
|
}
|
|
848
953
|
|
|
849
|
-
// src/ani/states.ts
|
|
954
|
+
// src/ani/raf/states.ts
|
|
850
955
|
function createStates(config) {
|
|
851
956
|
let State = config.initial;
|
|
852
|
-
let
|
|
957
|
+
let Timeline = rafTimeline(
|
|
853
958
|
config.states[State],
|
|
854
959
|
config.clock
|
|
855
960
|
);
|
|
856
961
|
const subs = /* @__PURE__ */ new Set();
|
|
857
|
-
const notify = (
|
|
962
|
+
const notify = (timeline) => {
|
|
858
963
|
for (const Sub of subs) {
|
|
859
|
-
Sub(
|
|
964
|
+
Sub(timeline);
|
|
860
965
|
}
|
|
861
966
|
};
|
|
862
967
|
return {
|
|
863
|
-
timeline: () =>
|
|
968
|
+
timeline: () => Timeline,
|
|
969
|
+
state: () => State,
|
|
864
970
|
onTimelineChange(callback) {
|
|
865
971
|
subs.add(callback);
|
|
866
972
|
return () => subs.delete(callback);
|
|
867
973
|
},
|
|
868
974
|
transitionTo(newState, timelineConfig, canBeIntercepted) {
|
|
869
|
-
if (!config.states[newState] || State === newState) {
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
975
|
const from = timelineConfig?.from ?? // 1. config
|
|
873
|
-
|
|
976
|
+
Timeline.getCurrentValue() ?? // 2. last value
|
|
874
977
|
config.initialFrom;
|
|
875
978
|
State = newState;
|
|
876
|
-
|
|
877
|
-
notify(
|
|
878
|
-
|
|
979
|
+
Timeline = rafTimeline(config.states[State], config.clock);
|
|
980
|
+
notify(Timeline);
|
|
981
|
+
Timeline.play(
|
|
879
982
|
{
|
|
880
983
|
...timelineConfig,
|
|
881
984
|
from
|
|
@@ -886,77 +989,6 @@ function createStates(config) {
|
|
|
886
989
|
};
|
|
887
990
|
}
|
|
888
991
|
|
|
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
992
|
// src/style/create_sheet.ts
|
|
961
993
|
var TransformFunctionMap = {
|
|
962
994
|
// deg
|
|
@@ -1044,41 +1076,286 @@ function createStyleSheet(animeStyleValue, resolver) {
|
|
|
1044
1076
|
return styleAccumulator;
|
|
1045
1077
|
}
|
|
1046
1078
|
|
|
1079
|
+
// src/ani/waapi/compiler/resolver.ts
|
|
1080
|
+
function resolveStateAt(plan, initialFrom, targetTime, dt) {
|
|
1081
|
+
const { keyMap, values: initialValues } = resolveGroup(initialFrom);
|
|
1082
|
+
const rawResultState = resolvePlanState(
|
|
1083
|
+
plan,
|
|
1084
|
+
initialValues,
|
|
1085
|
+
keyMap,
|
|
1086
|
+
targetTime,
|
|
1087
|
+
dt
|
|
1088
|
+
);
|
|
1089
|
+
return resolveStateToGroup(rawResultState, keyMap);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// src/ani/waapi/compiler/timing_compiler.ts
|
|
1093
|
+
function compileTiming(timing) {
|
|
1094
|
+
if (!timing) {
|
|
1095
|
+
return "linear";
|
|
1096
|
+
}
|
|
1097
|
+
if (timing instanceof LinearTimingFunction) {
|
|
1098
|
+
return "linear";
|
|
1099
|
+
}
|
|
1100
|
+
if (timing instanceof BezierTimingFunction) {
|
|
1101
|
+
const { p2, p3 } = timing.opt;
|
|
1102
|
+
return `cubic-bezier(${p2.x}, ${p2.y}, ${p3.x}, ${p3.y})`;
|
|
1103
|
+
}
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/ani/waapi/compiler/keyframe_compiler.ts
|
|
1108
|
+
function compileToKeyframes(plan, initialFrom) {
|
|
1109
|
+
if (plan.length === 0) {
|
|
1110
|
+
return [];
|
|
1111
|
+
}
|
|
1112
|
+
const FPS = 60;
|
|
1113
|
+
const SAMPLE_RATE = 1 / FPS;
|
|
1114
|
+
const duration = Math.max(...plan.map((s) => s.endTime));
|
|
1115
|
+
if (duration === 0) {
|
|
1116
|
+
const state = resolveStateAt(plan, initialFrom, 0, SAMPLE_RATE);
|
|
1117
|
+
const style = createStyleSheet(state);
|
|
1118
|
+
return [
|
|
1119
|
+
{ offset: 0, ...style },
|
|
1120
|
+
{ offset: 1, ...style }
|
|
1121
|
+
];
|
|
1122
|
+
}
|
|
1123
|
+
const timePoints = /* @__PURE__ */ new Set([0, duration]);
|
|
1124
|
+
for (const seg of plan) {
|
|
1125
|
+
timePoints.add(seg.startTime);
|
|
1126
|
+
timePoints.add(seg.endTime);
|
|
1127
|
+
}
|
|
1128
|
+
const sortedTimes = Array.from(timePoints).sort((a2, b) => a2 - b);
|
|
1129
|
+
const keyframes = [];
|
|
1130
|
+
const getEasingForInterval = (t, nextT) => {
|
|
1131
|
+
const activeSegments = plan.filter(
|
|
1132
|
+
(s) => s.startTime <= t && s.endTime >= nextT
|
|
1133
|
+
);
|
|
1134
|
+
if (activeSegments.length === 0) return "linear";
|
|
1135
|
+
const timings = activeSegments.map((s) => s.node.props.timing).filter((t2) => t2 !== void 0);
|
|
1136
|
+
if (timings.length === 0) return "linear";
|
|
1137
|
+
const firstTiming = timings[0];
|
|
1138
|
+
const allSame = timings.every((t2) => t2 === firstTiming);
|
|
1139
|
+
if (allSame && firstTiming instanceof TimingFunction) {
|
|
1140
|
+
return compileTiming(firstTiming);
|
|
1141
|
+
}
|
|
1142
|
+
return null;
|
|
1143
|
+
};
|
|
1144
|
+
for (let i = 0; i < sortedTimes.length; i++) {
|
|
1145
|
+
const currT = sortedTimes[i];
|
|
1146
|
+
const state = resolveStateAt(plan, initialFrom, currT, SAMPLE_RATE);
|
|
1147
|
+
const style = createStyleSheet(state);
|
|
1148
|
+
const keyframe = {
|
|
1149
|
+
offset: currT / duration,
|
|
1150
|
+
...style
|
|
1151
|
+
};
|
|
1152
|
+
keyframes.push(keyframe);
|
|
1153
|
+
if (i < sortedTimes.length - 1) {
|
|
1154
|
+
const nextT = sortedTimes[i + 1];
|
|
1155
|
+
const easing = getEasingForInterval(currT, nextT);
|
|
1156
|
+
if (easing === null) {
|
|
1157
|
+
let sampleT = currT + SAMPLE_RATE;
|
|
1158
|
+
while (sampleT < nextT) {
|
|
1159
|
+
const sampleState = resolveStateAt(
|
|
1160
|
+
plan,
|
|
1161
|
+
initialFrom,
|
|
1162
|
+
sampleT,
|
|
1163
|
+
SAMPLE_RATE
|
|
1164
|
+
);
|
|
1165
|
+
const sampleStyle = createStyleSheet(
|
|
1166
|
+
sampleState
|
|
1167
|
+
);
|
|
1168
|
+
keyframes.push({
|
|
1169
|
+
offset: sampleT / duration,
|
|
1170
|
+
...sampleStyle,
|
|
1171
|
+
easing: "linear"
|
|
1172
|
+
});
|
|
1173
|
+
sampleT += SAMPLE_RATE;
|
|
1174
|
+
}
|
|
1175
|
+
keyframe.easing = "linear";
|
|
1176
|
+
} else {
|
|
1177
|
+
keyframe.easing = easing;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return keyframes;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/ani/waapi/timeline.ts
|
|
1185
|
+
var WebAniTimeline = class extends TimelineBase {
|
|
1186
|
+
constructor(rootNode) {
|
|
1187
|
+
super(rootNode);
|
|
1188
|
+
this._animation = null;
|
|
1189
|
+
this._keyframes = [];
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Plays animation.
|
|
1193
|
+
* @param target Target element.
|
|
1194
|
+
* @param config {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API Web Animations API} based config.
|
|
1195
|
+
*/
|
|
1196
|
+
play(target, config) {
|
|
1197
|
+
if (this._animation) {
|
|
1198
|
+
this._animation.cancel();
|
|
1199
|
+
}
|
|
1200
|
+
this._currentExecutionPlan = this._resolveExecutionPlan(
|
|
1201
|
+
config.keyframes,
|
|
1202
|
+
config.durations
|
|
1203
|
+
);
|
|
1204
|
+
this._keyframes = compileToKeyframes(
|
|
1205
|
+
this._currentExecutionPlan,
|
|
1206
|
+
config.from
|
|
1207
|
+
);
|
|
1208
|
+
if (this._keyframes.length === 0) {
|
|
1209
|
+
return null;
|
|
1210
|
+
}
|
|
1211
|
+
const totalDurationMs = this._currentExecutionPlan.reduce(
|
|
1212
|
+
(max, seg) => Math.max(max, seg.endTime),
|
|
1213
|
+
0
|
|
1214
|
+
) * 1e3;
|
|
1215
|
+
const effect = new KeyframeEffect(target, this._keyframes, {
|
|
1216
|
+
duration: totalDurationMs,
|
|
1217
|
+
iterations: config.repeat ?? 1,
|
|
1218
|
+
delay: config.delay ?? 0,
|
|
1219
|
+
fill: "forwards"
|
|
1220
|
+
});
|
|
1221
|
+
this._animation = new Animation(effect, document.timeline);
|
|
1222
|
+
this._animation.play();
|
|
1223
|
+
return this._animation;
|
|
1224
|
+
}
|
|
1225
|
+
pause() {
|
|
1226
|
+
this._animation?.pause();
|
|
1227
|
+
}
|
|
1228
|
+
resume() {
|
|
1229
|
+
this._animation?.play();
|
|
1230
|
+
}
|
|
1231
|
+
reset() {
|
|
1232
|
+
this._animation?.cancel();
|
|
1233
|
+
this._animation = null;
|
|
1234
|
+
}
|
|
1235
|
+
seek(targetTime) {
|
|
1236
|
+
if (this._animation) {
|
|
1237
|
+
this._animation.currentTime = targetTime * 1e3;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Native animation object.
|
|
1242
|
+
*
|
|
1243
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Animation Animation}.
|
|
1244
|
+
*/
|
|
1245
|
+
get nativeAnimation() {
|
|
1246
|
+
return this._animation;
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
function webTimeline(rootNode) {
|
|
1250
|
+
return new WebAniTimeline(rootNode);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/event/manager.ts
|
|
1254
|
+
var EventManager = class _EventManager {
|
|
1255
|
+
constructor(supportedEvents) {
|
|
1256
|
+
this.supportedEvents = supportedEvents;
|
|
1257
|
+
this._element = null;
|
|
1258
|
+
this._animeGetter = null;
|
|
1259
|
+
this.setAnimeGetter = (animeGetter) => {
|
|
1260
|
+
this._animeGetter = animeGetter;
|
|
1261
|
+
};
|
|
1262
|
+
this.eventMap = /* @__PURE__ */ new Map();
|
|
1263
|
+
this.withAnimeValue = (listener) => {
|
|
1264
|
+
const withAnime = (e) => {
|
|
1265
|
+
listener(this.animeGetter(), e);
|
|
1266
|
+
};
|
|
1267
|
+
return withAnime;
|
|
1268
|
+
};
|
|
1269
|
+
this.add = (eventName, listener, options) => {
|
|
1270
|
+
const withAnime = this.withAnimeValue(listener);
|
|
1271
|
+
this.eventMap.set(eventName, withAnime);
|
|
1272
|
+
this.targetElement.addEventListener(
|
|
1273
|
+
eventName,
|
|
1274
|
+
this.eventMap.get(eventName),
|
|
1275
|
+
options
|
|
1276
|
+
);
|
|
1277
|
+
};
|
|
1278
|
+
this.cleanupOne = (eventName) => {
|
|
1279
|
+
const removeListener = this.eventMap.get(eventName);
|
|
1280
|
+
if (!removeListener) return false;
|
|
1281
|
+
this.targetElement.removeEventListener(eventName, removeListener);
|
|
1282
|
+
return true;
|
|
1283
|
+
};
|
|
1284
|
+
this.cleanupAll = () => {
|
|
1285
|
+
const clearResponse = [];
|
|
1286
|
+
for (const evtName of this.eventMap.keys()) {
|
|
1287
|
+
const res = this.cleanupOne(evtName);
|
|
1288
|
+
clearResponse.push(res);
|
|
1289
|
+
}
|
|
1290
|
+
return clearResponse.some((t) => t === false) === false;
|
|
1291
|
+
};
|
|
1292
|
+
this.attach = (handlers) => {
|
|
1293
|
+
Object.entries(handlers).forEach(([eventKey, handler]) => {
|
|
1294
|
+
this.add(
|
|
1295
|
+
_EventManager.getEvtKey(eventKey),
|
|
1296
|
+
handler
|
|
1297
|
+
);
|
|
1298
|
+
});
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
get targetElement() {
|
|
1302
|
+
if (!this._element) throw new Error("EventManger, bind element first");
|
|
1303
|
+
return this._element;
|
|
1304
|
+
}
|
|
1305
|
+
get animeGetter() {
|
|
1306
|
+
if (!this._animeGetter)
|
|
1307
|
+
throw new Error("EventManager, animeGetter should be provided");
|
|
1308
|
+
return this._animeGetter;
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* get pure `{event_name}`
|
|
1312
|
+
* @param key onX`{event_name}`
|
|
1313
|
+
*/
|
|
1314
|
+
static getEvtKey(key) {
|
|
1315
|
+
const removed = key.substring(2, key.length);
|
|
1316
|
+
const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
|
|
1317
|
+
return Capitalized;
|
|
1318
|
+
}
|
|
1319
|
+
bind(element) {
|
|
1320
|
+
this._element = element;
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1047
1324
|
// src/index.ts
|
|
1048
1325
|
var a = {
|
|
1049
1326
|
timing: T,
|
|
1327
|
+
dynamicTimeline: rafTimeline,
|
|
1328
|
+
timeline: webTimeline,
|
|
1329
|
+
/**
|
|
1330
|
+
* Create animation segment.
|
|
1331
|
+
*/
|
|
1050
1332
|
ani,
|
|
1051
|
-
|
|
1333
|
+
/**
|
|
1334
|
+
* Add delay
|
|
1335
|
+
*/
|
|
1052
1336
|
delay,
|
|
1053
1337
|
loop,
|
|
1054
1338
|
parallel,
|
|
1055
1339
|
sequence,
|
|
1056
1340
|
stagger,
|
|
1057
|
-
|
|
1341
|
+
createStates
|
|
1058
1342
|
};
|
|
1059
1343
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1060
1344
|
0 && (module.exports = {
|
|
1061
1345
|
AnimationClock,
|
|
1062
|
-
|
|
1063
|
-
CompositionNode,
|
|
1346
|
+
BezierTimingFunction,
|
|
1064
1347
|
EventManager,
|
|
1065
1348
|
LinearTimingFunction,
|
|
1066
|
-
|
|
1067
|
-
SegmentNode,
|
|
1068
|
-
SequenceNode,
|
|
1069
|
-
StaggerNode,
|
|
1349
|
+
RafAniTimeline,
|
|
1070
1350
|
T,
|
|
1071
|
-
|
|
1351
|
+
TimelineBase,
|
|
1072
1352
|
TimingFunction,
|
|
1353
|
+
WebAniTimeline,
|
|
1073
1354
|
a,
|
|
1074
|
-
|
|
1355
|
+
calculateSegmentState,
|
|
1356
|
+
compileToKeyframes,
|
|
1075
1357
|
createStates,
|
|
1076
1358
|
createStyleSheet,
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
parallel,
|
|
1080
|
-
sequence,
|
|
1081
|
-
stagger,
|
|
1082
|
-
timeline
|
|
1359
|
+
rafTimeline,
|
|
1360
|
+
webTimeline
|
|
1083
1361
|
});
|
|
1084
|
-
//# sourceMappingURL=index.cjs.map
|