@freestylejs/ani-core 1.0.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 ADDED
@@ -0,0 +1,988 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AnimationClock: () => AnimationClock,
24
+ AnimationNode: () => AnimationNode,
25
+ CompositionNode: () => CompositionNode,
26
+ EventManager: () => EventManager,
27
+ LinearTimingFunction: () => LinearTimingFunction,
28
+ ParallelNode: () => ParallelNode,
29
+ SegmentNode: () => SegmentNode,
30
+ SequenceNode: () => SequenceNode,
31
+ StaggerNode: () => StaggerNode,
32
+ T: () => T,
33
+ Timeline: () => Timeline,
34
+ TimingFunction: () => TimingFunction,
35
+ a: () => a,
36
+ ani: () => ani,
37
+ createStates: () => createStates,
38
+ createStyleSheet: () => createStyleSheet,
39
+ delay: () => delay,
40
+ loop: () => loop,
41
+ parallel: () => parallel,
42
+ sequence: () => sequence,
43
+ stagger: () => stagger,
44
+ timeline: () => timeline
45
+ });
46
+ module.exports = __toCommonJS(src_exports);
47
+
48
+ // src/ani/nodes/base.ts
49
+ var AnimationNode = class {
50
+ constructor(id) {
51
+ if (id) {
52
+ this.id = id;
53
+ }
54
+ }
55
+ };
56
+
57
+ // src/timing/function.ts
58
+ var TimingFunction = class _TimingFunction {
59
+ static {
60
+ this.DEFAULT_TOLERANCE = 0.01;
61
+ }
62
+ /**
63
+ * Checks whether the animation has ended.
64
+ *
65
+ * @param time - The current time.
66
+ * @param value - The computed value at the current time.
67
+ * @param context - The animation context.
68
+ * @returns {boolean} True if the animation is ended, false otherwise.
69
+ */
70
+ checkEnd(time, value, context, checkTimeOnly = true) {
71
+ const tol = context.tolerance !== void 0 ? context.tolerance : _TimingFunction.DEFAULT_TOLERANCE;
72
+ const timeCondition = time >= context.duration;
73
+ const end = checkTimeOnly ? timeCondition : timeCondition && Math.abs(context.to - value) <= tol;
74
+ return end;
75
+ }
76
+ };
77
+
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
+ // src/timing/bezier.ts
88
+ var BezierTimingFunction = class extends TimingFunction {
89
+ constructor(opt) {
90
+ super();
91
+ this.opt = opt;
92
+ this.p1 = {
93
+ x: 0,
94
+ y: 0
95
+ };
96
+ this.p4 = {
97
+ x: 1,
98
+ y: 1
99
+ };
100
+ }
101
+ _bezierFunction(t, duration) {
102
+ const end = duration || this.p4.y;
103
+ return (1 - t) ** 3 * this.p1.y + 3 * (1 - t) ** 2 * t * this.opt.p2.y + 3 * (1 - t) * t ** 2 * this.opt.p3.y + t ** 3 * end;
104
+ }
105
+ step(time, context) {
106
+ const f = this._bezierFunction(time, context.duration);
107
+ return {
108
+ value: f,
109
+ endOfAnimation: (context.duration ? time >= context.duration : time >= this.p4.x) && f >= context.to
110
+ };
111
+ }
112
+ };
113
+
114
+ // src/timing/spring.ts
115
+ var SpringTimingFunction = class extends TimingFunction {
116
+ constructor(opt) {
117
+ super();
118
+ this.opt = opt;
119
+ if (!this.opt.tolerance) {
120
+ const DEFAULT_TOLERANCE = 1e-3;
121
+ this.opt.tolerance = DEFAULT_TOLERANCE;
122
+ }
123
+ }
124
+ step(time, context) {
125
+ const m = this.opt.m;
126
+ const k = this.opt.k;
127
+ const c = this.opt.c;
128
+ const tolerance = this.opt.tolerance;
129
+ const d0 = context.from - context.to;
130
+ if (typeof d0 !== "number") {
131
+ throw new TypeError(
132
+ "Spring step function, needs initial displacement d(0) = context.start"
133
+ );
134
+ }
135
+ const wN = Math.sqrt(k / m);
136
+ const zetta = c / (2 * Math.sqrt(m * k));
137
+ const Ms = 1e3;
138
+ if (zetta < 1) {
139
+ const settlingTime = -1 / (zetta * wN) * Math.log(tolerance);
140
+ const t = context?.duration ? settlingTime / context.duration * time : time;
141
+ const C1 = d0;
142
+ const C2 = zetta / Math.sqrt(1 - zetta ** 2) * d0;
143
+ const ode = Math.exp(-zetta * wN * t) * (C1 * Math.cos(wN * Math.sqrt(1 - zetta ** 2) * t) + C2 * Math.sin(wN * Math.sqrt(1 - zetta ** 2) * t)) + context.to;
144
+ return {
145
+ value: ode,
146
+ endOfAnimation: t >= settlingTime && ode >= context.to
147
+ };
148
+ }
149
+ if (zetta === 1) {
150
+ const settlingTime = -1 / wN * Math.log(tolerance);
151
+ const t = context?.duration ? settlingTime / context.duration * time : time;
152
+ const C1 = d0;
153
+ const C2 = wN * d0;
154
+ const ode = (C1 + C2 * t) * Math.exp(-wN * t) + context.to;
155
+ return {
156
+ value: ode,
157
+ endOfAnimation: t >= settlingTime && Math.abs(ode - context.to) <= context.to / Ms
158
+ };
159
+ }
160
+ if (zetta > 1) {
161
+ const settlingTime = -1 / (zetta * wN) * Math.log(tolerance);
162
+ const t = context?.duration ? settlingTime / context.duration * time : time;
163
+ const delta = Math.sqrt(zetta ** 2 - 1);
164
+ const lambda1 = -zetta * wN + wN * delta;
165
+ const lambda2 = -zetta * wN - wN * delta;
166
+ const C2 = -lambda1 * d0 / (lambda2 - lambda1);
167
+ const C1 = d0 - C2;
168
+ const ode = C1 * Math.exp(lambda1 * t) + C2 * Math.exp(lambda2 * t) + context.to;
169
+ return {
170
+ value: ode,
171
+ endOfAnimation: t >= settlingTime && Math.abs(ode - context.to) <= context.to / Ms
172
+ };
173
+ }
174
+ throw new Error("invalid system");
175
+ }
176
+ };
177
+
178
+ // src/timing/index.ts
179
+ var T = {
180
+ /**
181
+ * Creates a new Bezier timing function instance.
182
+ *
183
+ * @param {Bezier.BezierTimingFunctionOpt} opt - Options for configuring the Bezier curve.
184
+ * A new instance of BezierTimingFunction.
185
+ */
186
+ bezier: (opt) => new BezierTimingFunction(opt),
187
+ /**
188
+ * Creates a new Spring timing function instance.
189
+ *
190
+ * @param {Spring.SpringTimingFunctionOpt} opt - Options for configuring the Spring timing function.
191
+ * A new instance of SpringTimingFunction.
192
+ */
193
+ spring: (opt) => new SpringTimingFunction(opt),
194
+ /**
195
+ * Creates linear timing function instance.
196
+ */
197
+ linear: () => new LinearTimingFunction()
198
+ };
199
+
200
+ // src/ani/nodes/segment.ts
201
+ var SegmentNode = class extends AnimationNode {
202
+ constructor(props, id) {
203
+ super(id);
204
+ this.type = "SEGMENT";
205
+ const nodeProps = {
206
+ to: props.to,
207
+ duration: props.duration ?? 0,
208
+ ...props.timing !== void 0 && { timing: props.timing }
209
+ };
210
+ this.props = nodeProps;
211
+ }
212
+ get duration() {
213
+ return this.props.duration;
214
+ }
215
+ construct(plan, startTime) {
216
+ plan.push({
217
+ node: this,
218
+ startTime,
219
+ endTime: startTime + this.duration
220
+ });
221
+ }
222
+ };
223
+ function ani(props, id) {
224
+ return new SegmentNode(props, id);
225
+ }
226
+
227
+ // src/ani/nodes/composition.ts
228
+ var CompositionNode = class _CompositionNode extends AnimationNode {
229
+ constructor(children, timing, id) {
230
+ super(id);
231
+ const parentTiming = timing ?? new LinearTimingFunction();
232
+ const adjustTiming = (children2) => {
233
+ return children2.map((child) => {
234
+ if (child instanceof SegmentNode && // child timing none >> override to parent
235
+ child.props.timing === void 0 && timing) {
236
+ return new SegmentNode(
237
+ {
238
+ ...child.props,
239
+ timing: parentTiming
240
+ },
241
+ child.id
242
+ );
243
+ }
244
+ if (child instanceof _CompositionNode) {
245
+ child.children = adjustTiming(child.children);
246
+ return child;
247
+ }
248
+ return child;
249
+ });
250
+ };
251
+ const timingOverridingChildren = adjustTiming(children);
252
+ this.children = timingOverridingChildren;
253
+ }
254
+ };
255
+
256
+ // src/ani/nodes/delay.ts
257
+ function delay(duration, id) {
258
+ return new SegmentNode({ to: {}, duration }, id);
259
+ }
260
+
261
+ // src/ani/nodes/loop.ts
262
+ var LoopNode = class extends CompositionNode {
263
+ constructor(child, loopCount, timing, id) {
264
+ super([child], timing, id);
265
+ this.type = "LOOP";
266
+ this.child = child;
267
+ this.count = loopCount;
268
+ this.duration = child.duration * loopCount;
269
+ }
270
+ construct(plan, startTime) {
271
+ let currentTime = startTime;
272
+ for (let i = 0; i < this.count; i++) {
273
+ this.child.construct(plan, currentTime);
274
+ currentTime += this.child.duration;
275
+ }
276
+ }
277
+ };
278
+ function loop(child, loopCount, timing, id) {
279
+ return new LoopNode(child, loopCount, timing, id);
280
+ }
281
+
282
+ // src/ani/nodes/parallel.ts
283
+ var ParallelNode = class extends CompositionNode {
284
+ constructor(children, timing, id) {
285
+ const seenProperty = /* @__PURE__ */ new Set();
286
+ const resolvedChildren = [];
287
+ for (const child of [...children].reverse()) {
288
+ if (child instanceof SegmentNode) {
289
+ const segment = child;
290
+ const propsTo = segment.props.to;
291
+ if (propsTo && !Array.isArray(propsTo)) {
292
+ const propsToAnimate = Object.keys(propsTo);
293
+ const finalConstructedTo = {};
294
+ let handledCount = 0;
295
+ for (const propKey of propsToAnimate) {
296
+ if (!seenProperty.has(propKey)) {
297
+ finalConstructedTo[propKey] = propsTo[propKey];
298
+ seenProperty.add(propKey);
299
+ handledCount++;
300
+ }
301
+ }
302
+ if (handledCount > 0) {
303
+ const newSegment = new SegmentNode(
304
+ {
305
+ ...segment.props,
306
+ to: finalConstructedTo
307
+ },
308
+ segment.id
309
+ );
310
+ resolvedChildren.push(newSegment);
311
+ }
312
+ continue;
313
+ }
314
+ }
315
+ resolvedChildren.push(child);
316
+ }
317
+ resolvedChildren.reverse();
318
+ super(resolvedChildren, timing, id);
319
+ this.type = "PARALLEL";
320
+ this.duration = Math.max(0, ...children.map((child) => child.duration));
321
+ }
322
+ construct(plan, startTime) {
323
+ for (const child of this.children) {
324
+ child.construct(plan, startTime);
325
+ }
326
+ }
327
+ };
328
+ function parallel(children, timing, id) {
329
+ return new ParallelNode(children, timing, id);
330
+ }
331
+
332
+ // src/ani/nodes/sequence.ts
333
+ var SequenceNode = class extends CompositionNode {
334
+ constructor(children, timing, id) {
335
+ super(children, timing, id);
336
+ this.type = "SEQUENCE";
337
+ this.duration = children.reduce((sum, child) => sum + child.duration, 0);
338
+ }
339
+ construct(plan, startTime) {
340
+ let currentTime = startTime;
341
+ for (const child of this.children) {
342
+ child.construct(plan, currentTime);
343
+ currentTime += child.duration;
344
+ }
345
+ }
346
+ };
347
+ function sequence(children, timing, id) {
348
+ return new SequenceNode(children, timing, id);
349
+ }
350
+
351
+ // src/ani/nodes/stagger.ts
352
+ var StaggerNode = class extends CompositionNode {
353
+ constructor(children, props, id) {
354
+ super(children, props?.timing, id);
355
+ this.type = "STAGGER";
356
+ this.offset = props.offset;
357
+ if (children.length === 0) {
358
+ this.duration = 0;
359
+ } else {
360
+ const lastChild = children[children.length - 1];
361
+ this.duration = (children.length - 1) * this.offset + lastChild.duration;
362
+ }
363
+ }
364
+ construct(plan, startTime) {
365
+ let currentTime = startTime;
366
+ for (const child of this.children) {
367
+ child.construct(plan, currentTime);
368
+ currentTime += this.offset;
369
+ }
370
+ }
371
+ };
372
+ function stagger(children, props, id) {
373
+ return new StaggerNode(children, props, id);
374
+ }
375
+
376
+ // src/loop/clock.ts
377
+ var AnimationClock = class _AnimationClock {
378
+ constructor(maxDeltaTime) {
379
+ this.subscribers = /* @__PURE__ */ new Set();
380
+ this.animationFrameId = null;
381
+ this.lastTimestamp = 0;
382
+ this.tick = (timestamp) => {
383
+ if (!this.animationFrameId) return;
384
+ const MS = 1e3;
385
+ let dt = (timestamp - this.lastTimestamp) / MS;
386
+ this.lastTimestamp = timestamp;
387
+ if (dt < 0) dt = 0;
388
+ if (dt > this.maxDeltaTime) {
389
+ dt = this.maxDeltaTime;
390
+ }
391
+ for (const subscriber of this.subscribers) {
392
+ subscriber.update(dt);
393
+ }
394
+ this.animationFrameId = window.requestAnimationFrame(this.tick);
395
+ };
396
+ this.maxDeltaTime = maxDeltaTime;
397
+ }
398
+ /**
399
+ * Crete new clock(singleton)
400
+ * @param maxDeltaTime default maxDt = 100ms
401
+ * @returns clock
402
+ */
403
+ static create(maxDeltaTime = 1 / 10) {
404
+ _AnimationClock.clock = new _AnimationClock(maxDeltaTime);
405
+ return _AnimationClock.clock;
406
+ }
407
+ subscribe(animatable) {
408
+ this.subscribers.add(animatable);
409
+ if (!this.animationFrameId) {
410
+ this.start();
411
+ }
412
+ }
413
+ unsubscribe(animatable) {
414
+ this.subscribers.delete(animatable);
415
+ if (this.subscribers.size === 0) {
416
+ this.stop();
417
+ }
418
+ }
419
+ start() {
420
+ this.lastTimestamp = performance.now();
421
+ this.animationFrameId = window.requestAnimationFrame(this.tick);
422
+ }
423
+ stop() {
424
+ if (this.animationFrameId) {
425
+ window.cancelAnimationFrame(this.animationFrameId);
426
+ this.animationFrameId = null;
427
+ }
428
+ }
429
+ };
430
+
431
+ // src/utils/time/is_end.ts
432
+ function isEndOfAnimation(currentT, duration, tolerance = 1e-3) {
433
+ return currentT === duration || currentT - duration >= tolerance;
434
+ }
435
+
436
+ // src/ani/engine.ts
437
+ function calculateSegmentState(localTime, segmentDef, dt = 0) {
438
+ const t = Math.max(0, Math.min(localTime, segmentDef.duration));
439
+ const animeValues = [];
440
+ let allComplete = true;
441
+ const isMultipleTiming = Array.isArray(segmentDef.timing);
442
+ if (isMultipleTiming && segmentDef.timing.length !== segmentDef.from.length) {
443
+ throw new TypeError(
444
+ `[calculateSegmentState] timing does not correctly set. It requires multiple timing for ${segmentDef.from}, but received ${segmentDef.timing}`
445
+ );
446
+ }
447
+ for (let i = 0; i < segmentDef.from.length; i++) {
448
+ const timingFunction = isMultipleTiming ? segmentDef.timing[i] : segmentDef.timing;
449
+ const animeResponse = timingFunction.step(t, {
450
+ dt,
451
+ from: segmentDef.from[i],
452
+ to: segmentDef.to[i],
453
+ duration: segmentDef.duration
454
+ });
455
+ animeValues.push(animeResponse.value);
456
+ if (!animeResponse.endOfAnimation) {
457
+ allComplete = false;
458
+ }
459
+ }
460
+ return {
461
+ values: animeValues,
462
+ isComplete: allComplete || isEndOfAnimation(t, segmentDef.duration)
463
+ };
464
+ }
465
+
466
+ // src/ani/timeline.ts
467
+ var Timeline = class {
468
+ constructor(rootNode, clock) {
469
+ this.rootNode = rootNode;
470
+ this._currentExecutionPlan = null;
471
+ this._masterTime = 0;
472
+ this._status = "IDLE";
473
+ this._currentConfig = null;
474
+ this._state = [];
475
+ this._initialState = [];
476
+ this._repeatCount = 0;
477
+ this._propertyKeyMap = null;
478
+ this._segmentStartStates = /* @__PURE__ */ new Map();
479
+ this._onUpdateCallbacks = /* @__PURE__ */ new Set();
480
+ this.duration = rootNode.duration;
481
+ this._baseExecutionPlan = this._constructExecutionPlan(rootNode);
482
+ this._clock = clock ?? AnimationClock.create();
483
+ this.play.bind(this);
484
+ this.pause.bind(this);
485
+ this.seek.bind(this);
486
+ this.resume.bind(this);
487
+ this.reset.bind(this);
488
+ }
489
+ /**
490
+ * Current animation running config.
491
+ */
492
+ get currentConfig() {
493
+ return this._currentConfig;
494
+ }
495
+ /**
496
+ * Resolves a Group (like {x, y}) into keys and values.
497
+ */
498
+ _resolveGroup(group) {
499
+ if (Array.isArray(group)) {
500
+ return { keyMap: null, values: group };
501
+ }
502
+ const keyMap = new Map(Object.keys(group).map((key, i) => [key, i]));
503
+ const values = Object.values(group);
504
+ return { keyMap, values };
505
+ }
506
+ /**
507
+ * Resolves the internal state (a number array) back into Group.
508
+ */
509
+ _resolveStateToGroup(state) {
510
+ if (!this._propertyKeyMap) {
511
+ return state;
512
+ }
513
+ const group = {};
514
+ let i = 0;
515
+ for (const key of this._propertyKeyMap.keys()) {
516
+ group[key] = state[i];
517
+ i++;
518
+ }
519
+ return group;
520
+ }
521
+ /**
522
+ * Compile animation execution plan
523
+ */
524
+ _constructExecutionPlan(rootNode) {
525
+ const plan = [];
526
+ rootNode.construct(plan, 0);
527
+ return plan;
528
+ }
529
+ /**
530
+ * Calculates the exact state of the animation at point.
531
+ */
532
+ _calculateStateAtTime(targetTime, dt = 0) {
533
+ if (this._initialState.length === 0 || !this._currentExecutionPlan) {
534
+ return [];
535
+ }
536
+ const nextState = [...this._initialState];
537
+ let stateAtLastStartTime = [...this._initialState];
538
+ for (const segment of this._currentExecutionPlan) {
539
+ if (targetTime < segment.startTime) {
540
+ continue;
541
+ }
542
+ if (!segment.node.props.timing) {
543
+ throw new Error(
544
+ `[Timeline] timing should be provided. Please specify timing using a.timing.(...). Check target segment: ${JSON.stringify(segment, null, 2)}.`,
545
+ { cause: segment }
546
+ );
547
+ }
548
+ stateAtLastStartTime = [...nextState];
549
+ const { keyMap, values: toValues } = this._resolveGroup(
550
+ segment.node.props.to
551
+ );
552
+ const isRecordAni = this._propertyKeyMap !== null && keyMap !== null;
553
+ let fromValues = [];
554
+ if (isRecordAni) {
555
+ for (const key of keyMap.keys()) {
556
+ fromValues.push(
557
+ stateAtLastStartTime[this._propertyKeyMap.get(key)]
558
+ );
559
+ }
560
+ } else {
561
+ fromValues = stateAtLastStartTime;
562
+ }
563
+ let finalAnimeValues = [];
564
+ const localTime = targetTime - segment.startTime;
565
+ const segmentDef = {
566
+ from: fromValues,
567
+ to: toValues,
568
+ duration: segment.node.duration,
569
+ timing: segment.node.props.timing
570
+ };
571
+ const segmentState = calculateSegmentState(
572
+ localTime,
573
+ segmentDef,
574
+ dt
575
+ );
576
+ if (segmentState.isComplete) {
577
+ finalAnimeValues = toValues;
578
+ } else {
579
+ finalAnimeValues = segmentState.values;
580
+ }
581
+ if (isRecordAni) {
582
+ let i = 0;
583
+ for (const key of keyMap.keys()) {
584
+ const stateIndex = this._propertyKeyMap.get(key);
585
+ if (stateIndex === -1) {
586
+ continue;
587
+ }
588
+ nextState[stateIndex] = finalAnimeValues[i];
589
+ i++;
590
+ }
591
+ } else {
592
+ for (let i = 0; i < finalAnimeValues.length; i++) {
593
+ nextState[i] = finalAnimeValues[i];
594
+ }
595
+ }
596
+ }
597
+ return nextState;
598
+ }
599
+ _resolveExecutionPlan(keyframes, durations) {
600
+ if (!keyframes && !durations) {
601
+ return [...this._baseExecutionPlan];
602
+ }
603
+ const segmentNodes = this._baseExecutionPlan.filter(
604
+ (segment) => segment.node.type === "SEGMENT"
605
+ );
606
+ const segLength = segmentNodes.length;
607
+ if (keyframes && keyframes.length !== segLength) {
608
+ throw new Error(
609
+ `Timeline keyframe mismatch: Expected ${segLength} keyframes, but received ${keyframes.length}.`
610
+ );
611
+ }
612
+ if (durations && durations.length !== segLength) {
613
+ throw new Error(
614
+ `Timeline keyframe mismatch: Expected ${segLength} durations, but received ${durations.length}.`
615
+ );
616
+ }
617
+ const newPlan = [];
618
+ let keyframeIndex = 0;
619
+ for (const segment of this._baseExecutionPlan) {
620
+ if (segment.node.type === "SEGMENT") {
621
+ const dynamicTo = keyframes?.[keyframeIndex];
622
+ const dynamicDuration = durations?.[keyframeIndex];
623
+ const newSegmentProps = {
624
+ ...segment.node.props,
625
+ // >> dynamic to
626
+ ...dynamicTo && {
627
+ to: dynamicTo === "keep" ? segment.node.props.to : dynamicTo
628
+ },
629
+ // >> dynamic duration
630
+ ...dynamicDuration && {
631
+ duration: dynamicDuration === "keep" ? segment.node.props.duration : dynamicDuration
632
+ }
633
+ };
634
+ const newSegment = new SegmentNode(
635
+ newSegmentProps,
636
+ segment.node.id
637
+ );
638
+ newPlan.push({ ...segment, node: newSegment });
639
+ keyframeIndex++;
640
+ } else {
641
+ newPlan.push({ ...segment });
642
+ }
643
+ }
644
+ return newPlan;
645
+ }
646
+ notify() {
647
+ for (const subscriber of this._onUpdateCallbacks) {
648
+ subscriber({
649
+ state: this._resolveStateToGroup(this._state),
650
+ status: this._status
651
+ });
652
+ }
653
+ }
654
+ play(config, canBeIntercepted = true) {
655
+ if (this._status === "PLAYING" && !canBeIntercepted) {
656
+ return;
657
+ }
658
+ const isRepeating = this._currentConfig?.repeat && this._currentConfig?.repeat >= 1;
659
+ const savedRepeatCount = isRepeating ? this._repeatCount : 0;
660
+ this.reset(false);
661
+ this._repeatCount = savedRepeatCount;
662
+ if (isRepeating && this._repeatCount >= config.repeat) {
663
+ this._repeatCount = 0;
664
+ return;
665
+ }
666
+ this._currentConfig = config;
667
+ this._currentExecutionPlan = this._resolveExecutionPlan(
668
+ config.keyframes,
669
+ config.durations
670
+ );
671
+ const { keyMap: keys, values } = this._resolveGroup(config.from);
672
+ this._propertyKeyMap = keys;
673
+ this._state = values;
674
+ this._initialState = values;
675
+ this._status = "PLAYING";
676
+ this._clock.subscribe(this);
677
+ this.update(0);
678
+ }
679
+ pause() {
680
+ this._status = "PAUSED";
681
+ this._clock.unsubscribe(this);
682
+ }
683
+ resume() {
684
+ if (this._status !== "PAUSED") return;
685
+ this._status = "PLAYING";
686
+ this._clock.subscribe(this);
687
+ }
688
+ reset(notify = true) {
689
+ this._status = "IDLE";
690
+ this._currentConfig = null;
691
+ this._masterTime = 0;
692
+ this._state = [];
693
+ this._initialState = [];
694
+ this._propertyKeyMap = null;
695
+ this._segmentStartStates.clear();
696
+ this._currentExecutionPlan = null;
697
+ this._clock.unsubscribe(this);
698
+ this._repeatCount = 0;
699
+ if (notify) {
700
+ this.notify();
701
+ }
702
+ }
703
+ seek(targetTime) {
704
+ if (this._status === "PLAYING" || this._status === "ENDED") {
705
+ this.pause();
706
+ }
707
+ const seekTime = Math.max(0, Math.min(targetTime, this.duration));
708
+ this._masterTime = seekTime;
709
+ this._state = this._calculateStateAtTime(seekTime, 0);
710
+ this.notify();
711
+ }
712
+ getCurrentValue() {
713
+ if (this._state.length === 0) return null;
714
+ return this._resolveStateToGroup(this._state);
715
+ }
716
+ onUpdate(callback) {
717
+ this._onUpdateCallbacks.add(callback);
718
+ return () => {
719
+ this._onUpdateCallbacks.delete(callback);
720
+ };
721
+ }
722
+ update(dt) {
723
+ if (this._status !== "PLAYING") {
724
+ return;
725
+ }
726
+ this._masterTime += dt;
727
+ if (this._masterTime >= this.duration) {
728
+ this._masterTime = this.duration;
729
+ }
730
+ this._state = this._calculateStateAtTime(this._masterTime, dt);
731
+ this.notify();
732
+ if (isEndOfAnimation(this._masterTime, this.duration)) {
733
+ this._repeatCount += 1;
734
+ if (!this._currentConfig) {
735
+ throw new Error(
736
+ `[Timeline] currentConfig can not be null when update(dt)`
737
+ );
738
+ }
739
+ const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
740
+ if (noRepeat) {
741
+ this._status = "ENDED";
742
+ this._clock.unsubscribe(this);
743
+ this.notify();
744
+ } else {
745
+ this.play(this._currentConfig);
746
+ }
747
+ }
748
+ }
749
+ };
750
+ function timeline(rootNode, clock) {
751
+ return new Timeline(rootNode, clock);
752
+ }
753
+
754
+ // src/ani/states.ts
755
+ function createStates(config) {
756
+ let State = config.initial;
757
+ let Timeline2 = timeline(
758
+ config.states[State],
759
+ config.clock
760
+ );
761
+ const subs = /* @__PURE__ */ new Set();
762
+ const notify = (timeline2) => {
763
+ for (const Sub of subs) {
764
+ Sub(timeline2);
765
+ }
766
+ };
767
+ return {
768
+ timeline: () => Timeline2,
769
+ onTimelineChange(callback) {
770
+ subs.add(callback);
771
+ return () => subs.delete(callback);
772
+ },
773
+ transitionTo(newState, timelineConfig, canBeIntercepted) {
774
+ if (!config.states[newState] || State === newState) {
775
+ return;
776
+ }
777
+ const from = timelineConfig?.from ?? // 1. config
778
+ Timeline2.getCurrentValue() ?? // 2. last value
779
+ config.initialFrom;
780
+ State = newState;
781
+ Timeline2 = timeline(config.states[State], config.clock);
782
+ notify(Timeline2);
783
+ Timeline2.play(
784
+ {
785
+ ...timelineConfig,
786
+ from
787
+ },
788
+ canBeIntercepted
789
+ );
790
+ }
791
+ };
792
+ }
793
+
794
+ // src/event/manager.ts
795
+ var EventManager = class _EventManager {
796
+ constructor(supportedEvents) {
797
+ this.supportedEvents = supportedEvents;
798
+ this._element = null;
799
+ this._animeGetter = null;
800
+ this.setAnimeGetter = (animeGetter) => {
801
+ this._animeGetter = animeGetter;
802
+ };
803
+ this.eventMap = /* @__PURE__ */ new Map();
804
+ this.withAnimeValue = (listener) => {
805
+ const withAnime = (e) => {
806
+ listener(this.animeGetter(), e);
807
+ };
808
+ return withAnime;
809
+ };
810
+ this.add = (eventName, listener, options) => {
811
+ const withAnime = this.withAnimeValue(listener);
812
+ this.eventMap.set(eventName, withAnime);
813
+ this.targetElement.addEventListener(
814
+ eventName,
815
+ this.eventMap.get(eventName),
816
+ options
817
+ );
818
+ };
819
+ this.cleanupOne = (eventName) => {
820
+ const removeListener = this.eventMap.get(eventName);
821
+ if (!removeListener) return false;
822
+ this.targetElement.removeEventListener(eventName, removeListener);
823
+ return true;
824
+ };
825
+ this.cleanupAll = () => {
826
+ const clearResponse = [];
827
+ for (const evtName of this.eventMap.keys()) {
828
+ const res = this.cleanupOne(evtName);
829
+ clearResponse.push(res);
830
+ }
831
+ return clearResponse.some((t) => t === false) === false;
832
+ };
833
+ this.attach = (handlers) => {
834
+ Object.entries(handlers).forEach(([eventKey, handler]) => {
835
+ this.add(
836
+ _EventManager.getEvtKey(eventKey),
837
+ handler
838
+ );
839
+ });
840
+ };
841
+ }
842
+ get targetElement() {
843
+ if (!this._element) throw new Error("EventManger, bind element first");
844
+ return this._element;
845
+ }
846
+ get animeGetter() {
847
+ if (!this._animeGetter)
848
+ throw new Error("EventManager, animeGetter should be provided");
849
+ return this._animeGetter;
850
+ }
851
+ /**
852
+ * get pure `{event_name}`
853
+ * @param key onX`{event_name}`
854
+ */
855
+ static getEvtKey(key) {
856
+ const removed = key.substring(2, key.length);
857
+ const Capitalized = `${removed[0].toLowerCase()}${removed.substring(1, key.length)}`;
858
+ return Capitalized;
859
+ }
860
+ bind(element) {
861
+ this._element = element;
862
+ }
863
+ };
864
+
865
+ // src/style/create_sheet.ts
866
+ var TransformFunctionMap = {
867
+ // deg
868
+ rotate: { fn: "rotate", unit: "deg" },
869
+ rotateX: { fn: "rotateX", unit: "deg" },
870
+ rotateY: { fn: "rotateY", unit: "deg" },
871
+ rotateZ: { fn: "rotateZ", unit: "deg" },
872
+ skew: { fn: "skew", unit: "deg" },
873
+ skewX: { fn: "skewX", unit: "deg" },
874
+ skewY: { fn: "skewY", unit: "deg" },
875
+ // px
876
+ translate: { fn: "translate", unit: "px" },
877
+ translateX: { fn: "translateX", unit: "px" },
878
+ translateY: { fn: "translateY", unit: "px" },
879
+ translateZ: { fn: "translateZ", unit: "px" },
880
+ // unitless
881
+ scale: { fn: "scale" },
882
+ scaleX: { fn: "scaleX" },
883
+ scaleY: { fn: "scaleY" },
884
+ scaleZ: { fn: "scaleZ" }
885
+ };
886
+ var TransformProperties = new Set(Object.keys(TransformFunctionMap));
887
+ var PxProperties = /* @__PURE__ */ new Set([
888
+ "width",
889
+ "height",
890
+ "margin",
891
+ "marginTop",
892
+ "marginBottom",
893
+ "marginLeft",
894
+ "marginRight",
895
+ "padding",
896
+ "paddingTop",
897
+ "paddingBottom",
898
+ "paddingLeft",
899
+ "paddingRight",
900
+ "top",
901
+ "left",
902
+ "right",
903
+ "bottom",
904
+ "borderWidth",
905
+ "borderRadius"
906
+ ]);
907
+ var UnitlessProperties = /* @__PURE__ */ new Set([
908
+ "opacity",
909
+ "zIndex",
910
+ "lineHeight",
911
+ "fontWeight"
912
+ ]);
913
+ function createStyleSheet(animeStyleValue, resolver) {
914
+ const styleAccumulator = {};
915
+ const CssTransformParts = [];
916
+ for (const key in animeStyleValue) {
917
+ if (!Object.hasOwn(animeStyleValue, key)) {
918
+ continue;
919
+ }
920
+ const value = animeStyleValue[key];
921
+ if (typeof value !== "number") {
922
+ continue;
923
+ }
924
+ const styleResolver = resolver?.[key];
925
+ if (styleResolver) {
926
+ const { key: resolvedKey, value: resolvedValue } = styleResolver(value);
927
+ styleAccumulator[resolvedKey] = resolvedValue;
928
+ continue;
929
+ }
930
+ if (TransformProperties.has(key)) {
931
+ const res = TransformFunctionMap[key];
932
+ const transformPart = "unit" in res ? `${res.fn}(${value}${res.unit})` : `${res.fn}(${value})`;
933
+ CssTransformParts.push(transformPart);
934
+ continue;
935
+ }
936
+ if (PxProperties.has(key)) {
937
+ styleAccumulator[key] = `${value}px`;
938
+ continue;
939
+ }
940
+ if (UnitlessProperties.has(key)) {
941
+ styleAccumulator[key] = value;
942
+ continue;
943
+ }
944
+ styleAccumulator[key] = value;
945
+ }
946
+ if (CssTransformParts.length > 0) {
947
+ styleAccumulator["transform"] = CssTransformParts.join(" ");
948
+ }
949
+ return styleAccumulator;
950
+ }
951
+
952
+ // src/index.ts
953
+ var a = {
954
+ timing: T,
955
+ ani,
956
+ createStates,
957
+ delay,
958
+ loop,
959
+ parallel,
960
+ sequence,
961
+ stagger,
962
+ timeline
963
+ };
964
+ // Annotate the CommonJS export names for ESM import in node:
965
+ 0 && (module.exports = {
966
+ AnimationClock,
967
+ AnimationNode,
968
+ CompositionNode,
969
+ EventManager,
970
+ LinearTimingFunction,
971
+ ParallelNode,
972
+ SegmentNode,
973
+ SequenceNode,
974
+ StaggerNode,
975
+ T,
976
+ Timeline,
977
+ TimingFunction,
978
+ a,
979
+ ani,
980
+ createStates,
981
+ createStyleSheet,
982
+ delay,
983
+ loop,
984
+ parallel,
985
+ sequence,
986
+ stagger,
987
+ timeline
988
+ });