@freestylejs/ani-core 1.2.1 → 1.3.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 CHANGED
@@ -34,7 +34,9 @@ __export(src_exports, {
34
34
  compileToKeyframes: () => compileToKeyframes,
35
35
  createStates: () => createStates,
36
36
  createStyleSheet: () => createStyleSheet,
37
+ normalizeRepeatCount: () => normalizeRepeatCount,
37
38
  rafTimeline: () => rafTimeline,
39
+ toIterationCount: () => toIterationCount,
38
40
  webTimeline: () => webTimeline
39
41
  });
40
42
  module.exports = __toCommonJS(src_exports);
@@ -436,6 +438,14 @@ function ani(props, id) {
436
438
  }
437
439
 
438
440
  // src/nodes/composition.ts
441
+ function cloneCompositionNodeWithChildren(source, children) {
442
+ const clone = Object.create(
443
+ Object.getPrototypeOf(source)
444
+ );
445
+ Object.assign(clone, source);
446
+ clone.children = children;
447
+ return clone;
448
+ }
439
449
  var CompositionNode = class _CompositionNode extends AnimationNode {
440
450
  constructor(children, timing, id) {
441
451
  super(id);
@@ -453,8 +463,11 @@ var CompositionNode = class _CompositionNode extends AnimationNode {
453
463
  );
454
464
  }
455
465
  if (child instanceof _CompositionNode) {
456
- child.children = adjustTiming(child.children);
457
- return child;
466
+ const adjustedChildren = adjustTiming(child.children);
467
+ return cloneCompositionNodeWithChildren(
468
+ child,
469
+ adjustedChildren
470
+ );
458
471
  }
459
472
  return child;
460
473
  });
@@ -585,6 +598,17 @@ function stagger(children, offset, timing, id) {
585
598
  }
586
599
 
587
600
  // src/ani/core/interface/timeline_interface.ts
601
+ function normalizeRepeatCount(repeat) {
602
+ if (repeat === Infinity) return Infinity;
603
+ if (!Number.isFinite(repeat) || repeat === void 0 || repeat <= 0) {
604
+ return 0;
605
+ }
606
+ return Math.floor(repeat);
607
+ }
608
+ function toIterationCount(repeat) {
609
+ const normalized = normalizeRepeatCount(repeat);
610
+ return normalized === Infinity ? Infinity : normalized + 1;
611
+ }
588
612
  var TimelineBase = class {
589
613
  constructor(rootNode) {
590
614
  this.rootNode = rootNode;
@@ -634,10 +658,10 @@ var TimelineBase = class {
634
658
  const dynamicDuration = durations?.[keyframeIndex];
635
659
  const newSegmentProps = {
636
660
  ...segment.node.props,
637
- ...dynamicTo && dynamicTo !== "keep" && {
661
+ ...dynamicTo !== void 0 && dynamicTo !== "keep" && {
638
662
  to: dynamicTo
639
663
  },
640
- ...dynamicDuration && dynamicDuration !== "keep" && {
664
+ ...dynamicDuration !== void 0 && dynamicDuration !== "keep" && {
641
665
  duration: dynamicDuration
642
666
  }
643
667
  };
@@ -683,9 +707,20 @@ var AnimationClock = class _AnimationClock {
683
707
  * @returns clock
684
708
  */
685
709
  static create(maxDeltaTime = 1 / 10) {
686
- _AnimationClock.clock = new _AnimationClock(maxDeltaTime);
710
+ if (!_AnimationClock.clock) {
711
+ _AnimationClock.clock = new _AnimationClock(maxDeltaTime);
712
+ }
687
713
  return _AnimationClock.clock;
688
714
  }
715
+ /**
716
+ * Reconfigure singleton clock.
717
+ * If not created yet, creates one with provided maxDeltaTime.
718
+ */
719
+ static configure(maxDeltaTime) {
720
+ const clock = _AnimationClock.create(maxDeltaTime);
721
+ clock.maxDeltaTime = maxDeltaTime;
722
+ return clock;
723
+ }
689
724
  subscribe(animatable) {
690
725
  this.subscribers.add(animatable);
691
726
  if (!this.animationFrameId) {
@@ -796,7 +831,7 @@ var RafAniTimeline = class extends TimelineBase {
796
831
  this._currentConfig = null;
797
832
  this._state = [];
798
833
  this._initialState = [];
799
- this._repeatCount = 0;
834
+ this._remainingRepeats = 0;
800
835
  this._propertyKeyMap = null;
801
836
  this._onUpdateCallbacks = /* @__PURE__ */ new Set();
802
837
  this._clock = clock ?? AnimationClock.create();
@@ -829,6 +864,26 @@ var RafAniTimeline = class extends TimelineBase {
829
864
  dt
830
865
  );
831
866
  }
867
+ _resolveRuntimeState(config) {
868
+ this._currentExecutionPlan = this._resolveExecutionPlan(
869
+ config.keyframes,
870
+ config.durations
871
+ );
872
+ const { keyMap, values } = resolveGroup(config.from);
873
+ this._propertyKeyMap = keyMap;
874
+ this._state = values;
875
+ this._initialState = values;
876
+ this._masterTime = 0;
877
+ }
878
+ _restartForRepeat() {
879
+ if (!this._currentConfig) {
880
+ return;
881
+ }
882
+ this._delay = 0;
883
+ this._resolveRuntimeState(this._currentConfig);
884
+ this._status = "PLAYING";
885
+ this.notify();
886
+ }
832
887
  notify() {
833
888
  for (const subscriber of this._onUpdateCallbacks) {
834
889
  subscriber({
@@ -859,14 +914,15 @@ var RafAniTimeline = class extends TimelineBase {
859
914
  this._state = this._calculateStateAtTime(this._masterTime, dt);
860
915
  this.notify();
861
916
  if (isEndOfAnimation(this._masterTime, this.duration)) {
862
- this._repeatCount += 1;
863
- const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
864
- if (noRepeat) {
917
+ if (this._remainingRepeats === Infinity || this._remainingRepeats > 0) {
918
+ if (this._remainingRepeats !== Infinity) {
919
+ this._remainingRepeats -= 1;
920
+ }
921
+ this._restartForRepeat();
922
+ } else {
865
923
  this._status = "ENDED";
866
924
  this._clock.unsubscribe(this);
867
925
  this.notify();
868
- } else {
869
- this.play(this._currentConfig);
870
926
  }
871
927
  }
872
928
  }
@@ -877,27 +933,11 @@ var RafAniTimeline = class extends TimelineBase {
877
933
  */
878
934
  play(config, canBeIntercepted = true) {
879
935
  if (this._status === "PLAYING" && !canBeIntercepted) return;
880
- const isRepeating = (this._currentConfig?.repeat ?? 0) >= 1;
881
- const savedRepeatCount = isRepeating ? this._repeatCount : 0;
882
- const isPlaying = this._status === "PLAYING";
883
- this.reset(false, !isPlaying);
884
- this._repeatCount = savedRepeatCount;
885
- if (isRepeating && this._repeatCount >= config.repeat) {
886
- this._repeatCount = 0;
887
- return;
888
- }
936
+ this.reset(false, true);
889
937
  this._currentConfig = config;
890
- if (this._repeatCount === 0) {
891
- this._delay = (this._currentConfig.delay ?? 0) * 1e-3;
892
- }
893
- this._currentExecutionPlan = this._resolveExecutionPlan(
894
- config.keyframes,
895
- config.durations
896
- );
897
- const { keyMap, values } = resolveGroup(config.from);
898
- this._propertyKeyMap = keyMap;
899
- this._state = values;
900
- this._initialState = values;
938
+ this._remainingRepeats = normalizeRepeatCount(config.repeat);
939
+ this._delay = (this._currentConfig.delay ?? 0) * 1e-3;
940
+ this._resolveRuntimeState(config);
901
941
  this._status = "PLAYING";
902
942
  this._clock.subscribe(this);
903
943
  this.notify();
@@ -923,7 +963,7 @@ var RafAniTimeline = class extends TimelineBase {
923
963
  if (unsubscribeClock) {
924
964
  this._clock.unsubscribe(this);
925
965
  }
926
- this._repeatCount = 0;
966
+ this._remainingRepeats = 0;
927
967
  if (notify) this.notify();
928
968
  }
929
969
  seek(targetTime) {
@@ -972,9 +1012,11 @@ function createStates(config) {
972
1012
  return () => subs.delete(callback);
973
1013
  },
974
1014
  transitionTo(newState, timelineConfig, canBeIntercepted) {
1015
+ const previousTimeline = Timeline;
975
1016
  const from = timelineConfig?.from ?? // 1. config
976
1017
  Timeline.getCurrentValue() ?? // 2. last value
977
1018
  config.initialFrom;
1019
+ previousTimeline.reset(false);
978
1020
  State = newState;
979
1021
  Timeline = rafTimeline(config.states[State], config.clock);
980
1022
  notify(Timeline);
@@ -1105,7 +1147,7 @@ function compileTiming(timing) {
1105
1147
  }
1106
1148
 
1107
1149
  // src/ani/waapi/compiler/keyframe_compiler.ts
1108
- function compileToKeyframes(plan, initialFrom) {
1150
+ function compileToKeyframes(plan, initialFrom, resolver) {
1109
1151
  if (plan.length === 0) {
1110
1152
  return [];
1111
1153
  }
@@ -1114,7 +1156,10 @@ function compileToKeyframes(plan, initialFrom) {
1114
1156
  const duration = Math.max(...plan.map((s) => s.endTime));
1115
1157
  if (duration === 0) {
1116
1158
  const state = resolveStateAt(plan, initialFrom, 0, SAMPLE_RATE);
1117
- const style = createStyleSheet(state);
1159
+ const style = createStyleSheet(
1160
+ state,
1161
+ resolver
1162
+ );
1118
1163
  return [
1119
1164
  { offset: 0, ...style },
1120
1165
  { offset: 1, ...style }
@@ -1144,7 +1189,10 @@ function compileToKeyframes(plan, initialFrom) {
1144
1189
  for (let i = 0; i < sortedTimes.length; i++) {
1145
1190
  const currT = sortedTimes[i];
1146
1191
  const state = resolveStateAt(plan, initialFrom, currT, SAMPLE_RATE);
1147
- const style = createStyleSheet(state);
1192
+ const style = createStyleSheet(
1193
+ state,
1194
+ resolver
1195
+ );
1148
1196
  const keyframe = {
1149
1197
  offset: currT / duration,
1150
1198
  ...style
@@ -1163,7 +1211,8 @@ function compileToKeyframes(plan, initialFrom) {
1163
1211
  SAMPLE_RATE
1164
1212
  );
1165
1213
  const sampleStyle = createStyleSheet(
1166
- sampleState
1214
+ sampleState,
1215
+ resolver
1167
1216
  );
1168
1217
  keyframes.push({
1169
1218
  offset: sampleT / duration,
@@ -1203,7 +1252,8 @@ var WebAniTimeline = class extends TimelineBase {
1203
1252
  );
1204
1253
  this._keyframes = compileToKeyframes(
1205
1254
  this._currentExecutionPlan,
1206
- config.from
1255
+ config.from,
1256
+ config.propertyResolver
1207
1257
  );
1208
1258
  if (this._keyframes.length === 0) {
1209
1259
  return null;
@@ -1214,9 +1264,10 @@ var WebAniTimeline = class extends TimelineBase {
1214
1264
  ) * 1e3;
1215
1265
  const effect = new KeyframeEffect(target, this._keyframes, {
1216
1266
  duration: totalDurationMs,
1217
- iterations: config.repeat ?? 1,
1267
+ iterations: toIterationCount(config.repeat),
1218
1268
  delay: config.delay ?? 0,
1219
- fill: "forwards"
1269
+ fill: "forwards",
1270
+ ...config.keyframeEffect
1220
1271
  });
1221
1272
  this._animation = new Animation(effect, document.timeline);
1222
1273
  this._animation.play();
@@ -1232,6 +1283,12 @@ var WebAniTimeline = class extends TimelineBase {
1232
1283
  this._animation?.cancel();
1233
1284
  this._animation = null;
1234
1285
  }
1286
+ /**
1287
+ * Alias of reset() for WAAPI naming parity.
1288
+ */
1289
+ cancel() {
1290
+ this.reset();
1291
+ }
1235
1292
  seek(targetTime) {
1236
1293
  if (this._animation) {
1237
1294
  this._animation.currentTime = targetTime * 1e3;
@@ -1279,6 +1336,7 @@ var EventManager = class _EventManager {
1279
1336
  const removeListener = this.eventMap.get(eventName);
1280
1337
  if (!removeListener) return false;
1281
1338
  this.targetElement.removeEventListener(eventName, removeListener);
1339
+ this.eventMap.delete(eventName);
1282
1340
  return true;
1283
1341
  };
1284
1342
  this.cleanupAll = () => {
@@ -1324,8 +1382,17 @@ var EventManager = class _EventManager {
1324
1382
  // src/index.ts
1325
1383
  var a = {
1326
1384
  timing: T,
1385
+ /**
1386
+ * @deprecated Use `rafTimeline` alias for explicit engine naming.
1387
+ */
1327
1388
  dynamicTimeline: rafTimeline,
1389
+ /**
1390
+ * @deprecated Use `waapiTimeline` alias for explicit engine naming.
1391
+ */
1328
1392
  timeline: webTimeline,
1393
+ webTimeline,
1394
+ rafTimeline,
1395
+ waapiTimeline: webTimeline,
1329
1396
  /**
1330
1397
  * Create animation segment.
1331
1398
  */
@@ -1356,6 +1423,8 @@ var a = {
1356
1423
  compileToKeyframes,
1357
1424
  createStates,
1358
1425
  createStyleSheet,
1426
+ normalizeRepeatCount,
1359
1427
  rafTimeline,
1428
+ toIterationCount,
1360
1429
  webTimeline
1361
1430
  });
package/dist/index.d.cts CHANGED
@@ -467,6 +467,15 @@ interface TimelineCommonConfig<G extends Groupable> {
467
467
  */
468
468
  propertyResolver?: G extends AnimePrimitive ? never : Resolver<G>;
469
469
  }
470
+ /**
471
+ * Normalize repeat count as "additional iterations after first play".
472
+ */
473
+ declare function normalizeRepeatCount(repeat?: number): number;
474
+ /**
475
+ * Converts repeat count to WAAPI iteration count.
476
+ * repeat=0 -> iterations=1, repeat=1 -> iterations=2
477
+ */
478
+ declare function toIterationCount(repeat?: number): number;
470
479
  declare abstract class TimelineBase<G extends Groupable> {
471
480
  protected readonly rootNode: AnimationNode<G>;
472
481
  readonly duration: number;
@@ -527,6 +536,11 @@ declare class AnimationClock implements AnimationClockInterface {
527
536
  * @returns clock
528
537
  */
529
538
  static create(maxDeltaTime?: number): AnimationClock;
539
+ /**
540
+ * Reconfigure singleton clock.
541
+ * If not created yet, creates one with provided maxDeltaTime.
542
+ */
543
+ static configure(maxDeltaTime: number): AnimationClock;
530
544
  private constructor();
531
545
  subscribe(animatable: Animatable): void;
532
546
  unsubscribe(animatable: Animatable): void;
@@ -561,12 +575,14 @@ declare class RafAniTimeline<G extends Groupable, Ctx = any> extends TimelineBas
561
575
  get currentConfig(): RafTimelineConfig<G, Ctx> | null;
562
576
  private _state;
563
577
  private _initialState;
564
- private _repeatCount;
578
+ private _remainingRepeats;
565
579
  private _propertyKeyMap;
566
580
  private _onUpdateCallbacks;
567
581
  constructor(rootNode: AnimationNode<G>, clock?: AnimationClockInterface);
568
582
  getCurrentValue(): AniGroup<G> | null;
569
583
  private _calculateStateAtTime;
584
+ private _resolveRuntimeState;
585
+ private _restartForRepeat;
570
586
  private notify;
571
587
  /**
572
588
  * @private Internal clock subscription callback.
@@ -659,7 +675,7 @@ interface WebAniKeyframe extends Record<string, string | number | null> {
659
675
  * @param plan The resolved execution plan (from TimelineBase).
660
676
  * @param initialFrom The initial state (values).
661
677
  */
662
- declare function compileToKeyframes<G extends Groupable>(plan: ExecutionPlan<G>, initialFrom: G): WebAniKeyframe[];
678
+ declare function compileToKeyframes<G extends Groupable>(plan: ExecutionPlan<G>, initialFrom: G, resolver?: Resolver<Record<string, number>>): WebAniKeyframe[];
663
679
 
664
680
  interface WebAniTimelineConfig<G extends Groupable> extends TimelineCommonConfig<G> {
665
681
  /**
@@ -682,6 +698,10 @@ declare class WebAniTimeline<G extends Groupable> extends TimelineBase<G> {
682
698
  pause(): void;
683
699
  resume(): void;
684
700
  reset(): void;
701
+ /**
702
+ * Alias of reset() for WAAPI naming parity.
703
+ */
704
+ cancel(): void;
685
705
  seek(targetTime: number): void;
686
706
  /**
687
707
  * Native animation object.
@@ -759,8 +779,17 @@ declare const a: {
759
779
  readonly easeOut: () => BezierTimingFunction;
760
780
  readonly easeInOut: () => BezierTimingFunction;
761
781
  };
782
+ /**
783
+ * @deprecated Use `rafTimeline` alias for explicit engine naming.
784
+ */
762
785
  readonly dynamicTimeline: typeof rafTimeline;
786
+ /**
787
+ * @deprecated Use `waapiTimeline` alias for explicit engine naming.
788
+ */
763
789
  readonly timeline: typeof webTimeline;
790
+ readonly webTimeline: typeof webTimeline;
791
+ readonly rafTimeline: typeof rafTimeline;
792
+ readonly waapiTimeline: typeof webTimeline;
764
793
  /**
765
794
  * Create animation segment.
766
795
  */
@@ -776,4 +805,4 @@ declare const a: {
776
805
  readonly createStates: typeof createStates;
777
806
  };
778
807
 
779
- export { type AniGroup, type AniRefContext, type AniRefProps, type Animatable, AnimationClock, type AnimationClockInterface, type AnimationStateShape, type AnimePrimitive, BezierTimingFunction, type BezierTimingFunctionOpt, type Coord, type EventHandler, type EventHandlerRegistration, type EventKey, EventManager, type ExecutionPlan, type ExecutionSegment, type GetTimeline, type Groupable, type GroupableRecord, type GroupableRecordKey, LinearTimingFunction, type OnUpdateCallback, RafAniTimeline, type RafTimelineConfig, type Resolver, type SegmentDefinition, type SegmentState, type SegmentTiming, type StateController, type StateProps, type StylesheetSupportedLiteral, T, TimelineBase, type TimelineCommonConfig, TimingFunction, type TimingFunctionContext, type WebAniKeyframe, WebAniTimeline, type WebAniTimelineConfig, a, calculateSegmentState, compileToKeyframes, createStates, createStyleSheet, rafTimeline, webTimeline };
808
+ export { type AniGroup, type AniRefContext, type AniRefProps, type Animatable, AnimationClock, type AnimationClockInterface, type AnimationStateShape, type AnimePrimitive, BezierTimingFunction, type BezierTimingFunctionOpt, type Coord, type EventHandler, type EventHandlerRegistration, type EventKey, EventManager, type ExecutionPlan, type ExecutionSegment, type GetTimeline, type Groupable, type GroupableRecord, type GroupableRecordKey, LinearTimingFunction, type OnUpdateCallback, RafAniTimeline, type RafTimelineConfig, type Resolver, type SegmentDefinition, type SegmentState, type SegmentTiming, type StateController, type StateProps, type StylesheetSupportedLiteral, T, TimelineBase, type TimelineCommonConfig, TimingFunction, type TimingFunctionContext, type WebAniKeyframe, WebAniTimeline, type WebAniTimelineConfig, a, calculateSegmentState, compileToKeyframes, createStates, createStyleSheet, normalizeRepeatCount, rafTimeline, toIterationCount, webTimeline };
package/dist/index.d.ts CHANGED
@@ -467,6 +467,15 @@ interface TimelineCommonConfig<G extends Groupable> {
467
467
  */
468
468
  propertyResolver?: G extends AnimePrimitive ? never : Resolver<G>;
469
469
  }
470
+ /**
471
+ * Normalize repeat count as "additional iterations after first play".
472
+ */
473
+ declare function normalizeRepeatCount(repeat?: number): number;
474
+ /**
475
+ * Converts repeat count to WAAPI iteration count.
476
+ * repeat=0 -> iterations=1, repeat=1 -> iterations=2
477
+ */
478
+ declare function toIterationCount(repeat?: number): number;
470
479
  declare abstract class TimelineBase<G extends Groupable> {
471
480
  protected readonly rootNode: AnimationNode<G>;
472
481
  readonly duration: number;
@@ -527,6 +536,11 @@ declare class AnimationClock implements AnimationClockInterface {
527
536
  * @returns clock
528
537
  */
529
538
  static create(maxDeltaTime?: number): AnimationClock;
539
+ /**
540
+ * Reconfigure singleton clock.
541
+ * If not created yet, creates one with provided maxDeltaTime.
542
+ */
543
+ static configure(maxDeltaTime: number): AnimationClock;
530
544
  private constructor();
531
545
  subscribe(animatable: Animatable): void;
532
546
  unsubscribe(animatable: Animatable): void;
@@ -561,12 +575,14 @@ declare class RafAniTimeline<G extends Groupable, Ctx = any> extends TimelineBas
561
575
  get currentConfig(): RafTimelineConfig<G, Ctx> | null;
562
576
  private _state;
563
577
  private _initialState;
564
- private _repeatCount;
578
+ private _remainingRepeats;
565
579
  private _propertyKeyMap;
566
580
  private _onUpdateCallbacks;
567
581
  constructor(rootNode: AnimationNode<G>, clock?: AnimationClockInterface);
568
582
  getCurrentValue(): AniGroup<G> | null;
569
583
  private _calculateStateAtTime;
584
+ private _resolveRuntimeState;
585
+ private _restartForRepeat;
570
586
  private notify;
571
587
  /**
572
588
  * @private Internal clock subscription callback.
@@ -659,7 +675,7 @@ interface WebAniKeyframe extends Record<string, string | number | null> {
659
675
  * @param plan The resolved execution plan (from TimelineBase).
660
676
  * @param initialFrom The initial state (values).
661
677
  */
662
- declare function compileToKeyframes<G extends Groupable>(plan: ExecutionPlan<G>, initialFrom: G): WebAniKeyframe[];
678
+ declare function compileToKeyframes<G extends Groupable>(plan: ExecutionPlan<G>, initialFrom: G, resolver?: Resolver<Record<string, number>>): WebAniKeyframe[];
663
679
 
664
680
  interface WebAniTimelineConfig<G extends Groupable> extends TimelineCommonConfig<G> {
665
681
  /**
@@ -682,6 +698,10 @@ declare class WebAniTimeline<G extends Groupable> extends TimelineBase<G> {
682
698
  pause(): void;
683
699
  resume(): void;
684
700
  reset(): void;
701
+ /**
702
+ * Alias of reset() for WAAPI naming parity.
703
+ */
704
+ cancel(): void;
685
705
  seek(targetTime: number): void;
686
706
  /**
687
707
  * Native animation object.
@@ -759,8 +779,17 @@ declare const a: {
759
779
  readonly easeOut: () => BezierTimingFunction;
760
780
  readonly easeInOut: () => BezierTimingFunction;
761
781
  };
782
+ /**
783
+ * @deprecated Use `rafTimeline` alias for explicit engine naming.
784
+ */
762
785
  readonly dynamicTimeline: typeof rafTimeline;
786
+ /**
787
+ * @deprecated Use `waapiTimeline` alias for explicit engine naming.
788
+ */
763
789
  readonly timeline: typeof webTimeline;
790
+ readonly webTimeline: typeof webTimeline;
791
+ readonly rafTimeline: typeof rafTimeline;
792
+ readonly waapiTimeline: typeof webTimeline;
764
793
  /**
765
794
  * Create animation segment.
766
795
  */
@@ -776,4 +805,4 @@ declare const a: {
776
805
  readonly createStates: typeof createStates;
777
806
  };
778
807
 
779
- export { type AniGroup, type AniRefContext, type AniRefProps, type Animatable, AnimationClock, type AnimationClockInterface, type AnimationStateShape, type AnimePrimitive, BezierTimingFunction, type BezierTimingFunctionOpt, type Coord, type EventHandler, type EventHandlerRegistration, type EventKey, EventManager, type ExecutionPlan, type ExecutionSegment, type GetTimeline, type Groupable, type GroupableRecord, type GroupableRecordKey, LinearTimingFunction, type OnUpdateCallback, RafAniTimeline, type RafTimelineConfig, type Resolver, type SegmentDefinition, type SegmentState, type SegmentTiming, type StateController, type StateProps, type StylesheetSupportedLiteral, T, TimelineBase, type TimelineCommonConfig, TimingFunction, type TimingFunctionContext, type WebAniKeyframe, WebAniTimeline, type WebAniTimelineConfig, a, calculateSegmentState, compileToKeyframes, createStates, createStyleSheet, rafTimeline, webTimeline };
808
+ export { type AniGroup, type AniRefContext, type AniRefProps, type Animatable, AnimationClock, type AnimationClockInterface, type AnimationStateShape, type AnimePrimitive, BezierTimingFunction, type BezierTimingFunctionOpt, type Coord, type EventHandler, type EventHandlerRegistration, type EventKey, EventManager, type ExecutionPlan, type ExecutionSegment, type GetTimeline, type Groupable, type GroupableRecord, type GroupableRecordKey, LinearTimingFunction, type OnUpdateCallback, RafAniTimeline, type RafTimelineConfig, type Resolver, type SegmentDefinition, type SegmentState, type SegmentTiming, type StateController, type StateProps, type StylesheetSupportedLiteral, T, TimelineBase, type TimelineCommonConfig, TimingFunction, type TimingFunctionContext, type WebAniKeyframe, WebAniTimeline, type WebAniTimelineConfig, a, calculateSegmentState, compileToKeyframes, createStates, createStyleSheet, normalizeRepeatCount, rafTimeline, toIterationCount, webTimeline };
package/dist/index.js CHANGED
@@ -395,6 +395,14 @@ function ani(props, id) {
395
395
  }
396
396
 
397
397
  // src/nodes/composition.ts
398
+ function cloneCompositionNodeWithChildren(source, children) {
399
+ const clone = Object.create(
400
+ Object.getPrototypeOf(source)
401
+ );
402
+ Object.assign(clone, source);
403
+ clone.children = children;
404
+ return clone;
405
+ }
398
406
  var CompositionNode = class _CompositionNode extends AnimationNode {
399
407
  constructor(children, timing, id) {
400
408
  super(id);
@@ -412,8 +420,11 @@ var CompositionNode = class _CompositionNode extends AnimationNode {
412
420
  );
413
421
  }
414
422
  if (child instanceof _CompositionNode) {
415
- child.children = adjustTiming(child.children);
416
- return child;
423
+ const adjustedChildren = adjustTiming(child.children);
424
+ return cloneCompositionNodeWithChildren(
425
+ child,
426
+ adjustedChildren
427
+ );
417
428
  }
418
429
  return child;
419
430
  });
@@ -544,6 +555,17 @@ function stagger(children, offset, timing, id) {
544
555
  }
545
556
 
546
557
  // src/ani/core/interface/timeline_interface.ts
558
+ function normalizeRepeatCount(repeat) {
559
+ if (repeat === Infinity) return Infinity;
560
+ if (!Number.isFinite(repeat) || repeat === void 0 || repeat <= 0) {
561
+ return 0;
562
+ }
563
+ return Math.floor(repeat);
564
+ }
565
+ function toIterationCount(repeat) {
566
+ const normalized = normalizeRepeatCount(repeat);
567
+ return normalized === Infinity ? Infinity : normalized + 1;
568
+ }
547
569
  var TimelineBase = class {
548
570
  constructor(rootNode) {
549
571
  this.rootNode = rootNode;
@@ -593,10 +615,10 @@ var TimelineBase = class {
593
615
  const dynamicDuration = durations?.[keyframeIndex];
594
616
  const newSegmentProps = {
595
617
  ...segment.node.props,
596
- ...dynamicTo && dynamicTo !== "keep" && {
618
+ ...dynamicTo !== void 0 && dynamicTo !== "keep" && {
597
619
  to: dynamicTo
598
620
  },
599
- ...dynamicDuration && dynamicDuration !== "keep" && {
621
+ ...dynamicDuration !== void 0 && dynamicDuration !== "keep" && {
600
622
  duration: dynamicDuration
601
623
  }
602
624
  };
@@ -642,9 +664,20 @@ var AnimationClock = class _AnimationClock {
642
664
  * @returns clock
643
665
  */
644
666
  static create(maxDeltaTime = 1 / 10) {
645
- _AnimationClock.clock = new _AnimationClock(maxDeltaTime);
667
+ if (!_AnimationClock.clock) {
668
+ _AnimationClock.clock = new _AnimationClock(maxDeltaTime);
669
+ }
646
670
  return _AnimationClock.clock;
647
671
  }
672
+ /**
673
+ * Reconfigure singleton clock.
674
+ * If not created yet, creates one with provided maxDeltaTime.
675
+ */
676
+ static configure(maxDeltaTime) {
677
+ const clock = _AnimationClock.create(maxDeltaTime);
678
+ clock.maxDeltaTime = maxDeltaTime;
679
+ return clock;
680
+ }
648
681
  subscribe(animatable) {
649
682
  this.subscribers.add(animatable);
650
683
  if (!this.animationFrameId) {
@@ -755,7 +788,7 @@ var RafAniTimeline = class extends TimelineBase {
755
788
  this._currentConfig = null;
756
789
  this._state = [];
757
790
  this._initialState = [];
758
- this._repeatCount = 0;
791
+ this._remainingRepeats = 0;
759
792
  this._propertyKeyMap = null;
760
793
  this._onUpdateCallbacks = /* @__PURE__ */ new Set();
761
794
  this._clock = clock ?? AnimationClock.create();
@@ -788,6 +821,26 @@ var RafAniTimeline = class extends TimelineBase {
788
821
  dt
789
822
  );
790
823
  }
824
+ _resolveRuntimeState(config) {
825
+ this._currentExecutionPlan = this._resolveExecutionPlan(
826
+ config.keyframes,
827
+ config.durations
828
+ );
829
+ const { keyMap, values } = resolveGroup(config.from);
830
+ this._propertyKeyMap = keyMap;
831
+ this._state = values;
832
+ this._initialState = values;
833
+ this._masterTime = 0;
834
+ }
835
+ _restartForRepeat() {
836
+ if (!this._currentConfig) {
837
+ return;
838
+ }
839
+ this._delay = 0;
840
+ this._resolveRuntimeState(this._currentConfig);
841
+ this._status = "PLAYING";
842
+ this.notify();
843
+ }
791
844
  notify() {
792
845
  for (const subscriber of this._onUpdateCallbacks) {
793
846
  subscriber({
@@ -818,14 +871,15 @@ var RafAniTimeline = class extends TimelineBase {
818
871
  this._state = this._calculateStateAtTime(this._masterTime, dt);
819
872
  this.notify();
820
873
  if (isEndOfAnimation(this._masterTime, this.duration)) {
821
- this._repeatCount += 1;
822
- const noRepeat = (this._currentConfig.repeat ?? 0) === 0;
823
- if (noRepeat) {
874
+ if (this._remainingRepeats === Infinity || this._remainingRepeats > 0) {
875
+ if (this._remainingRepeats !== Infinity) {
876
+ this._remainingRepeats -= 1;
877
+ }
878
+ this._restartForRepeat();
879
+ } else {
824
880
  this._status = "ENDED";
825
881
  this._clock.unsubscribe(this);
826
882
  this.notify();
827
- } else {
828
- this.play(this._currentConfig);
829
883
  }
830
884
  }
831
885
  }
@@ -836,27 +890,11 @@ var RafAniTimeline = class extends TimelineBase {
836
890
  */
837
891
  play(config, canBeIntercepted = true) {
838
892
  if (this._status === "PLAYING" && !canBeIntercepted) return;
839
- const isRepeating = (this._currentConfig?.repeat ?? 0) >= 1;
840
- const savedRepeatCount = isRepeating ? this._repeatCount : 0;
841
- const isPlaying = this._status === "PLAYING";
842
- this.reset(false, !isPlaying);
843
- this._repeatCount = savedRepeatCount;
844
- if (isRepeating && this._repeatCount >= config.repeat) {
845
- this._repeatCount = 0;
846
- return;
847
- }
893
+ this.reset(false, true);
848
894
  this._currentConfig = config;
849
- if (this._repeatCount === 0) {
850
- this._delay = (this._currentConfig.delay ?? 0) * 1e-3;
851
- }
852
- this._currentExecutionPlan = this._resolveExecutionPlan(
853
- config.keyframes,
854
- config.durations
855
- );
856
- const { keyMap, values } = resolveGroup(config.from);
857
- this._propertyKeyMap = keyMap;
858
- this._state = values;
859
- this._initialState = values;
895
+ this._remainingRepeats = normalizeRepeatCount(config.repeat);
896
+ this._delay = (this._currentConfig.delay ?? 0) * 1e-3;
897
+ this._resolveRuntimeState(config);
860
898
  this._status = "PLAYING";
861
899
  this._clock.subscribe(this);
862
900
  this.notify();
@@ -882,7 +920,7 @@ var RafAniTimeline = class extends TimelineBase {
882
920
  if (unsubscribeClock) {
883
921
  this._clock.unsubscribe(this);
884
922
  }
885
- this._repeatCount = 0;
923
+ this._remainingRepeats = 0;
886
924
  if (notify) this.notify();
887
925
  }
888
926
  seek(targetTime) {
@@ -931,9 +969,11 @@ function createStates(config) {
931
969
  return () => subs.delete(callback);
932
970
  },
933
971
  transitionTo(newState, timelineConfig, canBeIntercepted) {
972
+ const previousTimeline = Timeline;
934
973
  const from = timelineConfig?.from ?? // 1. config
935
974
  Timeline.getCurrentValue() ?? // 2. last value
936
975
  config.initialFrom;
976
+ previousTimeline.reset(false);
937
977
  State = newState;
938
978
  Timeline = rafTimeline(config.states[State], config.clock);
939
979
  notify(Timeline);
@@ -1064,7 +1104,7 @@ function compileTiming(timing) {
1064
1104
  }
1065
1105
 
1066
1106
  // src/ani/waapi/compiler/keyframe_compiler.ts
1067
- function compileToKeyframes(plan, initialFrom) {
1107
+ function compileToKeyframes(plan, initialFrom, resolver) {
1068
1108
  if (plan.length === 0) {
1069
1109
  return [];
1070
1110
  }
@@ -1073,7 +1113,10 @@ function compileToKeyframes(plan, initialFrom) {
1073
1113
  const duration = Math.max(...plan.map((s) => s.endTime));
1074
1114
  if (duration === 0) {
1075
1115
  const state = resolveStateAt(plan, initialFrom, 0, SAMPLE_RATE);
1076
- const style = createStyleSheet(state);
1116
+ const style = createStyleSheet(
1117
+ state,
1118
+ resolver
1119
+ );
1077
1120
  return [
1078
1121
  { offset: 0, ...style },
1079
1122
  { offset: 1, ...style }
@@ -1103,7 +1146,10 @@ function compileToKeyframes(plan, initialFrom) {
1103
1146
  for (let i = 0; i < sortedTimes.length; i++) {
1104
1147
  const currT = sortedTimes[i];
1105
1148
  const state = resolveStateAt(plan, initialFrom, currT, SAMPLE_RATE);
1106
- const style = createStyleSheet(state);
1149
+ const style = createStyleSheet(
1150
+ state,
1151
+ resolver
1152
+ );
1107
1153
  const keyframe = {
1108
1154
  offset: currT / duration,
1109
1155
  ...style
@@ -1122,7 +1168,8 @@ function compileToKeyframes(plan, initialFrom) {
1122
1168
  SAMPLE_RATE
1123
1169
  );
1124
1170
  const sampleStyle = createStyleSheet(
1125
- sampleState
1171
+ sampleState,
1172
+ resolver
1126
1173
  );
1127
1174
  keyframes.push({
1128
1175
  offset: sampleT / duration,
@@ -1162,7 +1209,8 @@ var WebAniTimeline = class extends TimelineBase {
1162
1209
  );
1163
1210
  this._keyframes = compileToKeyframes(
1164
1211
  this._currentExecutionPlan,
1165
- config.from
1212
+ config.from,
1213
+ config.propertyResolver
1166
1214
  );
1167
1215
  if (this._keyframes.length === 0) {
1168
1216
  return null;
@@ -1173,9 +1221,10 @@ var WebAniTimeline = class extends TimelineBase {
1173
1221
  ) * 1e3;
1174
1222
  const effect = new KeyframeEffect(target, this._keyframes, {
1175
1223
  duration: totalDurationMs,
1176
- iterations: config.repeat ?? 1,
1224
+ iterations: toIterationCount(config.repeat),
1177
1225
  delay: config.delay ?? 0,
1178
- fill: "forwards"
1226
+ fill: "forwards",
1227
+ ...config.keyframeEffect
1179
1228
  });
1180
1229
  this._animation = new Animation(effect, document.timeline);
1181
1230
  this._animation.play();
@@ -1191,6 +1240,12 @@ var WebAniTimeline = class extends TimelineBase {
1191
1240
  this._animation?.cancel();
1192
1241
  this._animation = null;
1193
1242
  }
1243
+ /**
1244
+ * Alias of reset() for WAAPI naming parity.
1245
+ */
1246
+ cancel() {
1247
+ this.reset();
1248
+ }
1194
1249
  seek(targetTime) {
1195
1250
  if (this._animation) {
1196
1251
  this._animation.currentTime = targetTime * 1e3;
@@ -1238,6 +1293,7 @@ var EventManager = class _EventManager {
1238
1293
  const removeListener = this.eventMap.get(eventName);
1239
1294
  if (!removeListener) return false;
1240
1295
  this.targetElement.removeEventListener(eventName, removeListener);
1296
+ this.eventMap.delete(eventName);
1241
1297
  return true;
1242
1298
  };
1243
1299
  this.cleanupAll = () => {
@@ -1283,8 +1339,17 @@ var EventManager = class _EventManager {
1283
1339
  // src/index.ts
1284
1340
  var a = {
1285
1341
  timing: T,
1342
+ /**
1343
+ * @deprecated Use `rafTimeline` alias for explicit engine naming.
1344
+ */
1286
1345
  dynamicTimeline: rafTimeline,
1346
+ /**
1347
+ * @deprecated Use `waapiTimeline` alias for explicit engine naming.
1348
+ */
1287
1349
  timeline: webTimeline,
1350
+ webTimeline,
1351
+ rafTimeline,
1352
+ waapiTimeline: webTimeline,
1288
1353
  /**
1289
1354
  * Create animation segment.
1290
1355
  */
@@ -1314,6 +1379,8 @@ export {
1314
1379
  compileToKeyframes,
1315
1380
  createStates,
1316
1381
  createStyleSheet,
1382
+ normalizeRepeatCount,
1317
1383
  rafTimeline,
1384
+ toIterationCount,
1318
1385
  webTimeline
1319
1386
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@freestylejs/ani-core",
3
3
  "author": "freestyle",
4
- "version": "1.2.1",
4
+ "version": "1.3.0",
5
5
  "description": "Core functionality for the Ani animation library.",
6
6
  "license": "MIT",
7
7
  "type": "module",