animejs 4.3.6 → 4.4.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.
Files changed (142) hide show
  1. package/README.md +8 -11
  2. package/dist/bundles/anime.esm.js +1033 -421
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +1038 -421
  5. package/dist/bundles/anime.umd.min.js +2 -2
  6. package/dist/modules/animatable/animatable.cjs +1 -1
  7. package/dist/modules/animatable/animatable.js +2 -2
  8. package/dist/modules/animatable/index.cjs +1 -1
  9. package/dist/modules/animatable/index.js +1 -1
  10. package/dist/modules/animation/additive.cjs +1 -1
  11. package/dist/modules/animation/additive.js +1 -1
  12. package/dist/modules/animation/animation.cjs +38 -16
  13. package/dist/modules/animation/animation.d.ts +2 -2
  14. package/dist/modules/animation/animation.js +42 -20
  15. package/dist/modules/animation/composition.cjs +1 -1
  16. package/dist/modules/animation/composition.js +3 -3
  17. package/dist/modules/animation/index.cjs +1 -1
  18. package/dist/modules/animation/index.js +1 -1
  19. package/dist/modules/core/clock.cjs +1 -1
  20. package/dist/modules/core/clock.js +1 -1
  21. package/dist/modules/core/colors.cjs +1 -1
  22. package/dist/modules/core/colors.js +1 -1
  23. package/dist/modules/core/consts.cjs +3 -9
  24. package/dist/modules/core/consts.d.ts +1 -5
  25. package/dist/modules/core/consts.js +4 -8
  26. package/dist/modules/core/globals.cjs +16 -5
  27. package/dist/modules/core/globals.d.ts +22 -1
  28. package/dist/modules/core/globals.js +18 -6
  29. package/dist/modules/core/helpers.cjs +7 -10
  30. package/dist/modules/core/helpers.js +8 -11
  31. package/dist/modules/core/render.cjs +66 -63
  32. package/dist/modules/core/render.js +68 -65
  33. package/dist/modules/core/styles.cjs +53 -32
  34. package/dist/modules/core/styles.d.ts +1 -0
  35. package/dist/modules/core/styles.js +55 -35
  36. package/dist/modules/core/targets.cjs +1 -1
  37. package/dist/modules/core/targets.js +1 -1
  38. package/dist/modules/core/transforms.cjs +129 -13
  39. package/dist/modules/core/transforms.d.ts +1 -0
  40. package/dist/modules/core/transforms.js +130 -15
  41. package/dist/modules/core/units.cjs +1 -1
  42. package/dist/modules/core/units.js +1 -1
  43. package/dist/modules/core/values.cjs +68 -8
  44. package/dist/modules/core/values.d.ts +5 -2
  45. package/dist/modules/core/values.js +69 -11
  46. package/dist/modules/draggable/draggable.cjs +1 -1
  47. package/dist/modules/draggable/draggable.js +1 -1
  48. package/dist/modules/draggable/index.cjs +1 -1
  49. package/dist/modules/draggable/index.js +1 -1
  50. package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
  51. package/dist/modules/easings/cubic-bezier/index.js +1 -1
  52. package/dist/modules/easings/eases/index.cjs +1 -1
  53. package/dist/modules/easings/eases/index.js +1 -1
  54. package/dist/modules/easings/eases/parser.cjs +1 -1
  55. package/dist/modules/easings/eases/parser.js +1 -1
  56. package/dist/modules/easings/index.cjs +1 -1
  57. package/dist/modules/easings/index.js +1 -1
  58. package/dist/modules/easings/irregular/index.cjs +1 -1
  59. package/dist/modules/easings/irregular/index.js +1 -1
  60. package/dist/modules/easings/linear/index.cjs +1 -1
  61. package/dist/modules/easings/linear/index.js +1 -1
  62. package/dist/modules/easings/none.cjs +1 -1
  63. package/dist/modules/easings/none.js +1 -1
  64. package/dist/modules/easings/spring/index.cjs +1 -1
  65. package/dist/modules/easings/spring/index.js +1 -1
  66. package/dist/modules/easings/steps/index.cjs +1 -1
  67. package/dist/modules/easings/steps/index.js +1 -1
  68. package/dist/modules/engine/engine.cjs +1 -1
  69. package/dist/modules/engine/engine.js +1 -1
  70. package/dist/modules/engine/index.cjs +1 -1
  71. package/dist/modules/engine/index.js +1 -1
  72. package/dist/modules/events/index.cjs +1 -1
  73. package/dist/modules/events/index.js +1 -1
  74. package/dist/modules/events/scroll.cjs +1 -1
  75. package/dist/modules/events/scroll.js +1 -1
  76. package/dist/modules/index.cjs +9 -1
  77. package/dist/modules/index.d.ts +1 -0
  78. package/dist/modules/index.js +4 -1
  79. package/dist/modules/layout/index.cjs +1 -1
  80. package/dist/modules/layout/index.js +1 -1
  81. package/dist/modules/layout/layout.cjs +29 -25
  82. package/dist/modules/layout/layout.d.ts +4 -3
  83. package/dist/modules/layout/layout.js +30 -26
  84. package/dist/modules/scope/index.cjs +1 -1
  85. package/dist/modules/scope/index.js +1 -1
  86. package/dist/modules/scope/scope.cjs +1 -1
  87. package/dist/modules/scope/scope.js +1 -1
  88. package/dist/modules/svg/drawable.cjs +1 -1
  89. package/dist/modules/svg/drawable.js +1 -1
  90. package/dist/modules/svg/helpers.cjs +1 -1
  91. package/dist/modules/svg/helpers.js +1 -1
  92. package/dist/modules/svg/index.cjs +1 -1
  93. package/dist/modules/svg/index.js +1 -1
  94. package/dist/modules/svg/morphto.cjs +3 -6
  95. package/dist/modules/svg/morphto.js +3 -6
  96. package/dist/modules/svg/motionpath.cjs +1 -1
  97. package/dist/modules/svg/motionpath.js +1 -1
  98. package/dist/modules/text/index.cjs +3 -1
  99. package/dist/modules/text/index.d.ts +1 -0
  100. package/dist/modules/text/index.js +2 -1
  101. package/dist/modules/text/scramble.cjs +272 -0
  102. package/dist/modules/text/scramble.d.ts +3 -0
  103. package/dist/modules/text/scramble.js +270 -0
  104. package/dist/modules/text/split.cjs +5 -5
  105. package/dist/modules/text/split.d.ts +5 -5
  106. package/dist/modules/text/split.js +5 -5
  107. package/dist/modules/timeline/index.cjs +1 -1
  108. package/dist/modules/timeline/index.js +1 -1
  109. package/dist/modules/timeline/position.cjs +1 -1
  110. package/dist/modules/timeline/position.js +1 -1
  111. package/dist/modules/timeline/timeline.cjs +36 -18
  112. package/dist/modules/timeline/timeline.d.ts +6 -5
  113. package/dist/modules/timeline/timeline.js +37 -19
  114. package/dist/modules/timer/index.cjs +1 -1
  115. package/dist/modules/timer/index.js +1 -1
  116. package/dist/modules/timer/timer.cjs +8 -12
  117. package/dist/modules/timer/timer.d.ts +2 -0
  118. package/dist/modules/timer/timer.js +9 -13
  119. package/dist/modules/types/index.d.ts +76 -8
  120. package/dist/modules/utils/chainable.cjs +8 -5
  121. package/dist/modules/utils/chainable.js +8 -5
  122. package/dist/modules/utils/index.cjs +5 -1
  123. package/dist/modules/utils/index.d.ts +1 -0
  124. package/dist/modules/utils/index.js +2 -1
  125. package/dist/modules/utils/number.cjs +1 -1
  126. package/dist/modules/utils/number.js +1 -1
  127. package/dist/modules/utils/random.cjs +1 -1
  128. package/dist/modules/utils/random.js +1 -1
  129. package/dist/modules/utils/stagger.cjs +117 -20
  130. package/dist/modules/utils/stagger.js +118 -21
  131. package/dist/modules/utils/target.cjs +1 -1
  132. package/dist/modules/utils/target.js +1 -1
  133. package/dist/modules/utils/time.cjs +5 -3
  134. package/dist/modules/utils/time.d.ts +1 -1
  135. package/dist/modules/utils/time.js +5 -3
  136. package/dist/modules/waapi/composition.cjs +1 -1
  137. package/dist/modules/waapi/composition.js +1 -1
  138. package/dist/modules/waapi/index.cjs +1 -1
  139. package/dist/modules/waapi/index.js +1 -1
  140. package/dist/modules/waapi/waapi.cjs +19 -20
  141. package/dist/modules/waapi/waapi.js +20 -21
  142. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - UMD bundle
3
- * @version v4.3.6
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -46,7 +46,8 @@
46
46
  * @callback StaggerFunction
47
47
  * @param {Target} [target]
48
48
  * @param {Number} [index]
49
- * @param {Number} [length]
49
+ * @param {TargetsArray} [targets]
50
+ * @param {Tween|null} [prevTween]
50
51
  * @param {Timeline} [tl]
51
52
  * @return {T}
52
53
  */
@@ -54,9 +55,9 @@
54
55
  /**
55
56
  * @typedef {Object} StaggerParams
56
57
  * @property {Number|String} [start]
57
- * @property {Number|'first'|'center'|'last'|'random'} [from]
58
+ * @property {Number|'first'|'center'|'last'|'random'|Array.<Number>} [from]
58
59
  * @property {Boolean} [reversed]
59
- * @property {Array.<Number>} [grid]
60
+ * @property {Array.<Number>|Boolean} [grid]
60
61
  * @property {('x'|'y')} [axis]
61
62
  * @property {String|((target: Target, i: Number, length: Number) => Number)} [use]
62
63
  * @property {Number} [total]
@@ -117,8 +118,8 @@
117
118
 
118
119
  // A hack to get both ease names suggestions AND allow any strings
119
120
  // https://github.com/microsoft/TypeScript/issues/29729#issuecomment-460346421
120
- /** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring} EasingParam */
121
- /** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring} WAAPIEasingParam */
121
+ /** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring|TweakRegister} EasingParam */
122
+ /** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring|TweakRegister} WAAPIEasingParam */
122
123
 
123
124
  // Spring types
124
125
 
@@ -174,6 +175,7 @@
174
175
  * @property {Boolean|ScrollObserver} [autoplay]
175
176
  * @property {Number} [frameRate]
176
177
  * @property {Number} [playbackRate]
178
+ * @property {Number} [priority]
177
179
  */
178
180
 
179
181
  /**
@@ -184,9 +186,10 @@
184
186
 
185
187
  /**
186
188
  * @callback FunctionValue
187
- * @param {Target} target - The animated target
188
- * @param {Number} index - The target index
189
- * @param {Number} length - The total number of animated targets
189
+ * @param {Target} [target] - The animated target
190
+ * @param {Number} [index] - The target index
191
+ * @param {TargetsArray} [targets] - The array of all animated targets
192
+ * @param {Tween|null} [prevTween] - The previous sibling tween for the same target and property
190
193
  * @return {Number|String|TweenObjectValue|EasingParam|Array.<Number|String|TweenObjectValue>}
191
194
  */
192
195
 
@@ -355,7 +358,7 @@
355
358
  * - `'label'` - Label: Position animation at a named label position (e.g., `'My Label'`)<br>
356
359
  * - `stagger(String|Nummber)` - Stagger multi-elements animation positions (e.g., 10, 20, 30...)
357
360
  *
358
- * @typedef {TimelinePosition | StaggerFunction<Number|String>} TimelineAnimationPosition
361
+ * @typedef {TimelinePosition | StaggerFunction<Number|String> | TweakRegister} TimelineAnimationPosition
359
362
  */
360
363
 
361
364
  /**
@@ -379,7 +382,7 @@
379
382
  * @callback WAAPIFunctionValue
380
383
  * @param {DOMTarget} target - The animated target
381
384
  * @param {Number} index - The target index
382
- * @param {Number} length - The total number of animated targets
385
+ * @param {DOMTargetsArray} targets - The array of all animated targets
383
386
  * @return {WAAPITweenValue|WAAPIEasingParam}
384
387
  */
385
388
 
@@ -626,6 +629,26 @@
626
629
  * @property {Boolean} [debug]
627
630
  */
628
631
 
632
+ /**
633
+ * @typedef {Object} ScrambleTextParams
634
+ * @property {String|function(Target, Number, TargetsArray): String} [text] - the text to transition to, otherwise uses the original text
635
+ * @property {String|function(Target, Number, TargetsArray): String} [chars] - the characters used for scramble; named sets: 'lowercase', 'uppercase', 'numbers', 'symbols', 'braille', 'blocks', 'shades'; range syntax: 'A-Z', 'a-z0-9'; defaults to 'a-zA-Z0-9!%#_'
636
+ * @property {EasingParam} [ease] - the easing applied to the scramble animation
637
+ * @property {Number|'left'|'center'|'right'|'random'|'auto'} [from] - where the reveal wave starts from, 'auto' (default) uses 'left' when text grows and 'right' when it shrinks
638
+ * @property {Boolean} [reversed] - reverses the reveal order, so 'center' reveals from edges inward instead of center outward
639
+ * @property {Boolean|Number|String} [cursor] - characters displayed at the leading edge of the reveal wave; true uses '_', a number is a char code, a string is used directly
640
+ * @property {Number} [perturbation] - adds random timing offsets to each character's start and end, creating a more organic reveal
641
+ * @property {Number} [seed] - a seed for the random number generator to produce reproducible scramble sequences
642
+ * @property {Boolean|String} [override] - controls the starting appearance: false shows original text, true scrambles it (default), '' starts from blank, ' ' replaces characters with spaces, a custom string (supports range syntax like 'A-Z') uses its characters as scramble set
643
+ * @property {Number} [revealRate] - characters per second entering the active zone; higher values make the reveal wave move faster (default: 60)
644
+ * @property {Number} [settleDuration] - time in ms each character spends scrambling before settling into its final glyph (default: 300)
645
+ * @property {Number} [settleRate] - how many times per second scramble characters cycle in the active zone (default: 30)
646
+ * @property {Number|function(Target, Number, TargetsArray): Number} [duration] - if set to a value greater than 0, overrides the computed duration from interval and settle; if unset or 0, duration is calculated automatically from text length and timing parameters
647
+ * @property {Number|function(Target, Number, TargetsArray): Number} [revealDelay] - delay in ms before the reveal wave starts within the scramble animation
648
+ * @property {Number|function(Target, Number, TargetsArray): Number} [delay] - delay in ms before the entire scramble animation starts
649
+ * @property {function(String, Number): void} [onChange] - callback fired each time a character changes during scramble; receives the current scrambled text and the eased progress (0-1)
650
+ */
651
+
629
652
  // SVG types
630
653
 
631
654
  /**
@@ -647,7 +670,7 @@
647
670
  // TODO: Do we need to check if we're running inside a worker ?
648
671
  const isBrowser = typeof window !== 'undefined';
649
672
 
650
- /** @typedef {Window & {AnimeJS: Array} & {AnimeJSDevTools: any}|null} AnimeJSWindow
673
+ /** @typedef {Window & {AnimeJS: Array}|null} AnimeJSWindow
651
674
 
652
675
  /** @type {AnimeJSWindow} */
653
676
  const win = isBrowser ? /** @type {AnimeJSWindow} */(/** @type {unknown} */(window)) : null;
@@ -694,7 +717,6 @@
694
717
  const isDomSymbol = Symbol();
695
718
  const isSvgSymbol = Symbol();
696
719
  const transformsSymbol = Symbol();
697
- const morphPointsSymbol = Symbol();
698
720
  const proxyTargetSymbol = Symbol();
699
721
 
700
722
  // Numbers
@@ -718,6 +740,7 @@
718
740
  })();
719
741
 
720
742
  const validTransforms = [
743
+ 'perspective',
721
744
  'translateX',
722
745
  'translateY',
723
746
  'translateZ',
@@ -732,9 +755,6 @@
732
755
  'skew',
733
756
  'skewX',
734
757
  'skewY',
735
- 'matrix',
736
- 'matrix3d',
737
- 'perspective',
738
758
  ];
739
759
 
740
760
  const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({...a, [v]: v + '('}), {});
@@ -757,12 +777,23 @@
757
777
  // export const unitsExecRgx = /^([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)+([a-z]+|%)$/i;
758
778
  const unitsExecRgx = /^([-+]?\d*\.?\d+(?:e[-+]?\d+)?)([a-z]+|%)$/i;
759
779
  const lowerCaseRgx = /([a-z])([A-Z])/g;
760
- const transformsExecRgx = /(\w+)(\([^)]+\)+)/g; // Match inline transforms with cacl() values, returns the value wrapped in ()
761
780
  const relativeValuesExecRgx = /(\*=|\+=|-=)/;
762
781
  const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/;
763
782
 
764
783
 
765
784
 
785
+ /**
786
+ * @typedef {Object} EditorGlobals
787
+ * @property {boolean} showPanel
788
+ * @property {boolean} synced
789
+ * @property {Function} addAnimation
790
+ * @property {Function} addTimeline
791
+ * @property {Function} addTimelineChild
792
+ * @property {Function} resolveStagger
793
+ * @property {Object|null} _head
794
+ * @property {Object|null} _tail
795
+ */
796
+
766
797
  /** @type {DefaultsParams} */
767
798
  const defaults = {
768
799
  id: null,
@@ -806,11 +837,11 @@
806
837
  timeScale: 1,
807
838
  /** @type {Number} */
808
839
  tickThreshold: 200,
840
+ /** @type {EditorGlobals|null} */
841
+ editor: null,
809
842
  };
810
843
 
811
- const devTools = isBrowser && win.AnimeJSDevTools;
812
-
813
- const globalVersions = { version: '4.3.6', engine: null };
844
+ const globalVersions = { version: '4.4.0', engine: null };
814
845
 
815
846
  if (isBrowser) {
816
847
  if (!win.AnimeJS) win.AnimeJS = [];
@@ -926,8 +957,6 @@
926
957
  */
927
958
  const clamp$1 = (v, min, max) => v < min ? min : v > max ? max : v;
928
959
 
929
- const powCache = {};
930
-
931
960
  /**
932
961
  * Rounds a number to specified decimal places
933
962
  *
@@ -935,13 +964,12 @@
935
964
  * @param {Number} decimalLength - Number of decimal places
936
965
  * @return {Number}
937
966
  */
938
- const round$1 = (v, decimalLength) => {
939
- if (decimalLength < 0) return v;
940
- if (!decimalLength) return _round(v);
941
- let p = powCache[decimalLength];
942
- if (!p) p = powCache[decimalLength] = 10 ** decimalLength;
943
- return _round(v * p) / p;
944
- };
967
+ const round$1 = (v, decimalLength) => {
968
+ if (decimalLength < 0) return v;
969
+ if (!decimalLength) return _round(v);
970
+ const p = 10 ** decimalLength;
971
+ return _round(v * p) / p;
972
+ };
945
973
 
946
974
  /**
947
975
  * Snaps a value to nearest increment or array value
@@ -1072,28 +1100,143 @@
1072
1100
  */
1073
1101
  const parseInlineTransforms = (target, propName, animationInlineStyles) => {
1074
1102
  const inlineTransforms = target.style.transform;
1075
- let inlinedStylesPropertyValue;
1076
1103
  if (inlineTransforms) {
1077
1104
  const cachedTransforms = target[transformsSymbol];
1078
- let t; while (t = transformsExecRgx.exec(inlineTransforms)) {
1079
- const inlinePropertyName = t[1];
1080
- // const inlinePropertyValue = t[2];
1081
- const inlinePropertyValue = t[2].slice(1, -1);
1082
- cachedTransforms[inlinePropertyName] = inlinePropertyValue;
1083
- if (inlinePropertyName === propName) {
1084
- inlinedStylesPropertyValue = inlinePropertyValue;
1085
- // Store the new parsed inline styles if animationInlineStyles is provided
1086
- if (animationInlineStyles) {
1087
- animationInlineStyles[propName] = inlinePropertyValue;
1105
+ let pos = 0;
1106
+ const len = inlineTransforms.length;
1107
+ let fullTranslateValue;
1108
+ while (pos < len) {
1109
+ // Skip whitespace
1110
+ while (pos < len && inlineTransforms.charCodeAt(pos) === 32) pos++;
1111
+ if (pos >= len) break;
1112
+ // Read function name
1113
+ const nameStart = pos;
1114
+ while (pos < len && inlineTransforms.charCodeAt(pos) !== 40) pos++;
1115
+ if (pos >= len) break;
1116
+ const name = inlineTransforms.substring(nameStart, pos);
1117
+ // Scan to closing paren, recording top-level comma positions
1118
+ let depth = 1;
1119
+ const valueStart = pos + 1;
1120
+ let c1 = -1, c2 = -1;
1121
+ pos++;
1122
+ while (pos < len && depth > 0) {
1123
+ const c = inlineTransforms.charCodeAt(pos);
1124
+ if (c === 40) depth++;
1125
+ else if (c === 41) depth--;
1126
+ else if (c === 44 && depth === 1) {
1127
+ if (c1 === -1) c1 = pos;
1128
+ else if (c2 === -1) c2 = pos;
1129
+ }
1130
+ pos++;
1131
+ }
1132
+ const valueEnd = pos - 1;
1133
+ // Decompose multi-arg functions into individual axis properties
1134
+ if (name === 'translate' || name === 'translate3d') {
1135
+ if (c1 === -1) {
1136
+ cachedTransforms.translateX = inlineTransforms.substring(valueStart, valueEnd).trim();
1137
+ } else {
1138
+ cachedTransforms.translateX = inlineTransforms.substring(valueStart, c1).trim();
1139
+ if (c2 === -1) {
1140
+ cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
1141
+ } else {
1142
+ cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, c2).trim();
1143
+ cachedTransforms.translateZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
1144
+ }
1088
1145
  }
1146
+ fullTranslateValue = inlineTransforms.substring(valueStart, valueEnd);
1147
+ } else if (name === 'scale' || name === 'scale3d') {
1148
+ if (c1 === -1) {
1149
+ cachedTransforms.scale = inlineTransforms.substring(valueStart, valueEnd).trim();
1150
+ } else {
1151
+ cachedTransforms.scaleX = inlineTransforms.substring(valueStart, c1).trim();
1152
+ if (c2 === -1) {
1153
+ cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
1154
+ } else {
1155
+ cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, c2).trim();
1156
+ cachedTransforms.scaleZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
1157
+ }
1158
+ }
1159
+ } else {
1160
+ cachedTransforms[name] = inlineTransforms.substring(valueStart, valueEnd);
1089
1161
  }
1090
1162
  }
1163
+ // Resolve the requested property from the cache
1164
+ if (propName === 'translate3d' && fullTranslateValue) {
1165
+ if (animationInlineStyles) animationInlineStyles[propName] = fullTranslateValue;
1166
+ return fullTranslateValue;
1167
+ }
1168
+ const cached = cachedTransforms[propName];
1169
+ if (!isUnd(cached)) {
1170
+ if (animationInlineStyles) animationInlineStyles[propName] = cached;
1171
+ return cached;
1172
+ }
1091
1173
  }
1092
- return inlineTransforms && !isUnd(inlinedStylesPropertyValue) ? inlinedStylesPropertyValue :
1174
+ return propName === 'translate3d' ? '0px, 0px, 0px' :
1175
+ propName === 'rotate3d' ? '0, 0, 0, 0deg' :
1093
1176
  stringStartsWith(propName, 'scale') ? '1' :
1094
1177
  stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew') ? '0deg' : '0px';
1095
1178
  };
1096
1179
 
1180
+ /**
1181
+ * Builds a CSS transform string from the target's cached transform properties.
1182
+ * Iterates validTransforms in order (perspective > translate > rotate > scale > skew > matrix).
1183
+ * When adjacent axis properties are all present, emits a shorter shorthand (translateX + translateY -> translate(x, y))
1184
+ * The index is advanced past consumed properties so they are not emitted twice.
1185
+ * Properties without a grouping partner (e.g. translateY alone, scaleZ alone) emit individually.
1186
+ *
1187
+ * @param {Record<String, String>} props
1188
+ * @return {String}
1189
+ */
1190
+ const buildTransformString = (props) => {
1191
+ let str = emptyString;
1192
+ for (let i = 0, l = validTransforms.length; i < l; i++) {
1193
+ const key = validTransforms[i];
1194
+ const val = props[key];
1195
+ if (val !== undefined) {
1196
+ // Group translateX with adjacent translateY / translateZ
1197
+ if (key === 'translateX') {
1198
+ const next = props.translateY;
1199
+ if (next !== undefined) {
1200
+ const next2 = props.translateZ;
1201
+ if (next2 !== undefined) {
1202
+ str += `translate3d(${val},${next},${next2}) `;
1203
+ i += 2;
1204
+ } else {
1205
+ str += `translate(${val},${next}) `;
1206
+ i += 1;
1207
+ }
1208
+ continue;
1209
+ }
1210
+ }
1211
+ // Group scaleX with adjacent scaleY / scaleZ (only when standalone scale is absent)
1212
+ if (key === 'scaleX' && props.scale === undefined) {
1213
+ const next = props.scaleY;
1214
+ if (next !== undefined) {
1215
+ const next2 = props.scaleZ;
1216
+ if (next2 !== undefined) {
1217
+ str += `scale3d(${val},${next},${next2}) `;
1218
+ i += 2;
1219
+ } else {
1220
+ str += `scale(${val},${next}) `;
1221
+ i += 1;
1222
+ }
1223
+ continue;
1224
+ }
1225
+ }
1226
+ // All other properties: emit individually using pre-built fragment string
1227
+ str += `${transformsFragmentStrings[key]}${val}) `;
1228
+ }
1229
+ // Preserve non-animatable rotate3d in correct position (after rotateZ, before scale)
1230
+ if (key === 'rotateZ') {
1231
+ if (props.rotate3d !== undefined) str += `rotate3d(${props.rotate3d}) `;
1232
+ }
1233
+ }
1234
+ // Preserve non-animatable matrix/matrix3d from inline styles
1235
+ if (props.matrix !== undefined) str += `matrix(${props.matrix}) `;
1236
+ if (props.matrix3d !== undefined) str += `matrix3d(${props.matrix3d}) `;
1237
+ return str;
1238
+ };
1239
+
1097
1240
 
1098
1241
 
1099
1242
  /**
@@ -1195,15 +1338,16 @@
1195
1338
  * @param {TweenPropValue} value
1196
1339
  * @param {Target} target
1197
1340
  * @param {Number} index
1198
- * @param {Number} total
1199
- * @param {Object} [store]
1341
+ * @param {TargetsArray} targets
1342
+ * @param {Object|null} store
1343
+ * @param {Tween|null} prevTween
1200
1344
  * @return {any}
1201
1345
  */
1202
- const getFunctionValue = (value, target, index, total, store) => {
1346
+ const getFunctionValue = (value, target, index, targets, store, prevTween) => {
1203
1347
  let func;
1204
1348
  if (isFnc(value)) {
1205
1349
  func = () => {
1206
- const computed = /** @type {Function} */(value)(target, index, total);
1350
+ const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
1207
1351
  // Fallback to 0 if the function returns undefined / NaN / null / false / 0
1208
1352
  return !isNaN(+computed) ? +computed : computed || 0;
1209
1353
  };
@@ -1270,9 +1414,17 @@
1270
1414
  */
1271
1415
  const getOriginalAnimatableValue = (target, propName, tweenType, animationInlineStyles) => {
1272
1416
  const type = !isUnd(tweenType) ? tweenType : getTweenType(target, propName);
1273
- return type === tweenTypes.OBJECT ? target[propName] || 0 :
1274
- type === tweenTypes.ATTRIBUTE ? /** @type {DOMTarget} */(target).getAttribute(propName) :
1275
- type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
1417
+ if (type === tweenTypes.OBJECT) {
1418
+ const value = target[propName];
1419
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1420
+ return value || 0;
1421
+ }
1422
+ if (type === tweenTypes.ATTRIBUTE) {
1423
+ const value = /** @type {DOMTarget} */(target).getAttribute(propName);
1424
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1425
+ return value;
1426
+ }
1427
+ return type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
1276
1428
  type === tweenTypes.CSS_VAR ? getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles).trimStart() :
1277
1429
  getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles);
1278
1430
  };
@@ -1374,6 +1526,54 @@
1374
1526
 
1375
1527
  const decomposedOriginalValue = createDecomposedValueTargetObject();
1376
1528
 
1529
+ /**
1530
+ * @param {Tween} tween
1531
+ * @param {Number} progress
1532
+ * @param {Number} precision
1533
+ * @return {String}
1534
+ */
1535
+ const composeColorValue = (tween, progress, precision) => {
1536
+ const mod = tween._modifier;
1537
+ const fn = tween._fromNumbers;
1538
+ const tn = tween._toNumbers;
1539
+ const r = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[0], tn[0], progress))), 0, 255), 0);
1540
+ const g = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[1], tn[1], progress))), 0, 255), 0);
1541
+ const b = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[2], tn[2], progress))), 0, 255), 0);
1542
+ const a = clamp$1(/** @type {Number} */(mod(round$1(lerp$1(fn[3], tn[3], progress), precision))), 0, 1);
1543
+ if (tween._composition !== compositionTypes.none) {
1544
+ const ns = tween._numbers;
1545
+ ns[0] = r;
1546
+ ns[1] = g;
1547
+ ns[2] = b;
1548
+ ns[3] = a;
1549
+ }
1550
+ return `rgba(${r},${g},${b},${a})`;
1551
+ };
1552
+
1553
+ /**
1554
+ * @param {Tween} tween
1555
+ * @param {Number} progress
1556
+ * @param {Number} precision
1557
+ * @return {String}
1558
+ */
1559
+ const composeComplexValue = (tween, progress, precision) => {
1560
+ const mod = tween._modifier;
1561
+ const fn = tween._fromNumbers;
1562
+ const tn = tween._toNumbers;
1563
+ const ts = tween._strings;
1564
+ const hasComposition = tween._composition !== compositionTypes.none;
1565
+ let v = ts[0];
1566
+ for (let j = 0, l = tn.length; j < l; j++) {
1567
+ const n = /** @type {Number} */(mod(round$1(lerp$1(fn[j], tn[j], progress), precision)));
1568
+ const s = ts[j + 1];
1569
+ v += `${s ? n + s : n}`;
1570
+ if (hasComposition) {
1571
+ tween._numbers[j] = n;
1572
+ }
1573
+ }
1574
+ return v;
1575
+ };
1576
+
1377
1577
 
1378
1578
 
1379
1579
 
@@ -1402,7 +1602,6 @@
1402
1602
  const _hasChildren = tickable._hasChildren;
1403
1603
  const tickableDelay = tickable._delay;
1404
1604
  const tickablePrevAbsoluteTime = tickable._currentTime; // TODO: rename ._currentTime to ._absoluteCurrentTime
1405
-
1406
1605
  const tickableEndTime = tickableDelay + iterationDuration;
1407
1606
  const tickableAbsoluteTime = time - tickableDelay;
1408
1607
  const tickablePrevTime = clamp$1(tickablePrevAbsoluteTime, -tickableDelay, duration);
@@ -1534,30 +1733,9 @@
1534
1733
  number = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
1535
1734
  value = `${number}${tween._unit}`;
1536
1735
  } else if (tweenValueType === valueTypes.COLOR) {
1537
- const fn = tween._fromNumbers;
1538
- const tn = tween._toNumbers;
1539
- const r = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[0], tn[0], tweenProgress))), 0, 255), 0);
1540
- const g = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[1], tn[1], tweenProgress))), 0, 255), 0);
1541
- const b = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[2], tn[2], tweenProgress))), 0, 255), 0);
1542
- const a = clamp$1(/** @type {Number} */(tweenModifier(round$1(lerp$1(fn[3], tn[3], tweenProgress), tweenPrecision))), 0, 1);
1543
- value = `rgba(${r},${g},${b},${a})`;
1544
- if (tweenHasComposition) {
1545
- const ns = tween._numbers;
1546
- ns[0] = r;
1547
- ns[1] = g;
1548
- ns[2] = b;
1549
- ns[3] = a;
1550
- }
1736
+ value = composeColorValue(tween, tweenProgress, tweenPrecision);
1551
1737
  } else if (tweenValueType === valueTypes.COMPLEX) {
1552
- value = tween._strings[0];
1553
- for (let j = 0, l = tween._toNumbers.length; j < l; j++) {
1554
- const n = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumbers[j], tween._toNumbers[j], tweenProgress), tweenPrecision)));
1555
- const s = tween._strings[j + 1];
1556
- value += `${s ? n + s : n}`;
1557
- if (tweenHasComposition) {
1558
- tween._numbers[j] = n;
1559
- }
1560
- }
1738
+ value = composeComplexValue(tween, tweenProgress, tweenPrecision);
1561
1739
  }
1562
1740
 
1563
1741
  // For additive tweens and Animatables
@@ -1600,14 +1778,8 @@
1600
1778
 
1601
1779
  }
1602
1780
 
1603
- // NOTE: Possible improvement: Use translate(x,y) / translate3d(x,y,z) syntax
1604
- // to reduce memory usage on string composition
1605
1781
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
1606
- let str = emptyString;
1607
- for (let key in tweenTargetTransformsProperties) {
1608
- str += `${transformsFragmentStrings[key]}${tweenTargetTransformsProperties[key]}) `;
1609
- }
1610
- tweenStyle.transform = str;
1782
+ tweenStyle.transform = buildTransformString(tweenTargetTransformsProperties);
1611
1783
  tweenTransformsNeedUpdate = 0;
1612
1784
  }
1613
1785
 
@@ -1664,6 +1836,50 @@
1664
1836
  return hasRendered;
1665
1837
  };
1666
1838
 
1839
+ // Shared context for extracted forEachChildren callbacks in tick()
1840
+ // Avoids closure allocation every frame
1841
+
1842
+ let renderCtxChildrenTime = 0;
1843
+ let renderCtxTlFps = 0;
1844
+ let renderCtxTickTime = 0;
1845
+ let renderCtxTickMode = 0;
1846
+ let renderCtxMuteCallbacks = 0;
1847
+ let renderCtxInternalRender = 0;
1848
+ let renderCtxChildrenHasRendered = 0;
1849
+ let renderCtxChildrenHaveCompleted = true;
1850
+ let loopCtxIsRunningBackwards = false;
1851
+ let loopCtxIterationDuration = 0;
1852
+ let loopCtxMuteCallbacks = 0;
1853
+
1854
+ /** @param {JSAnimation} child */
1855
+ const tickLoopChild = (child) => {
1856
+ if (!loopCtxIsRunningBackwards) {
1857
+ // Force an internal render to trigger the callbacks if the child has not completed on loop
1858
+ if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
1859
+ render(child, loopCtxIterationDuration, loopCtxMuteCallbacks, 1, tickModes.FORCE);
1860
+ }
1861
+ // Reset their began and completed flags to allow retrigering callbacks on the next iteration
1862
+ child.began = false;
1863
+ child.completed = false;
1864
+ } else {
1865
+ const childDuration = child.duration;
1866
+ const childStartTime = child._offset + child._delay;
1867
+ const childEndTime = childStartTime + childDuration;
1868
+ // Triggers the onComplete callback on reverse for children on the edges of the timeline
1869
+ if (!loopCtxMuteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === loopCtxIterationDuration)) {
1870
+ child.onComplete(child);
1871
+ }
1872
+ }
1873
+ };
1874
+
1875
+ /** @param {JSAnimation} child */
1876
+ const tickRenderChild = (child) => {
1877
+ const childTime = round$1((renderCtxChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
1878
+ const childTickMode = child._fps < renderCtxTlFps ? child.requestTick(renderCtxTickTime) : renderCtxTickMode;
1879
+ renderCtxChildrenHasRendered += render(child, childTime, renderCtxMuteCallbacks, renderCtxInternalRender, childTickMode);
1880
+ if (!child.completed && renderCtxChildrenHaveCompleted) renderCtxChildrenHaveCompleted = false;
1881
+ };
1882
+
1667
1883
  /**
1668
1884
  * @param {Tickable} tickable
1669
1885
  * @param {Number} time
@@ -1680,45 +1896,30 @@
1680
1896
  const tlIsRunningBackwards = tl.backwards;
1681
1897
  const tlChildrenTime = internalRender ? time : tl._iterationTime;
1682
1898
  const tlCildrenTickTime = now();
1683
-
1684
1899
  let tlChildrenHasRendered = 0;
1685
1900
  let tlChildrenHaveCompleted = true;
1686
-
1687
1901
  // If the timeline has looped forward, we need to manually triggers children skipped callbacks
1688
1902
  if (!internalRender && tl._currentIteration !== _currentIteration) {
1689
1903
  const tlIterationDuration = tl.iterationDuration;
1690
- forEachChildren(tl, (/** @type {JSAnimation} */child) => {
1691
- if (!tlIsRunningBackwards) {
1692
- // Force an internal render to trigger the callbacks if the child has not completed on loop
1693
- if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
1694
- render(child, tlIterationDuration, muteCallbacks, 1, tickModes.FORCE);
1695
- }
1696
- // Reset their began and completed flags to allow retrigering callbacks on the next iteration
1697
- child.began = false;
1698
- child.completed = false;
1699
- } else {
1700
- const childDuration = child.duration;
1701
- const childStartTime = child._offset + child._delay;
1702
- const childEndTime = childStartTime + childDuration;
1703
- // Triggers the onComplete callback on reverse for children on the edges of the timeline
1704
- if (!muteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === tlIterationDuration)) {
1705
- child.onComplete(child);
1706
- }
1707
- }
1708
- });
1904
+ loopCtxIsRunningBackwards = tlIsRunningBackwards;
1905
+ loopCtxIterationDuration = tlIterationDuration;
1906
+ loopCtxMuteCallbacks = muteCallbacks;
1907
+ forEachChildren(tl, tickLoopChild);
1709
1908
  if (!muteCallbacks) tl.onLoop(/** @type {CallbackArgument} */(tl));
1710
1909
  }
1711
-
1712
- forEachChildren(tl, (/** @type {JSAnimation} */child) => {
1713
- const childTime = round$1((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
1714
- const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
1715
- tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
1716
- if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
1717
- }, tlIsRunningBackwards);
1718
-
1910
+ renderCtxChildrenTime = tlChildrenTime;
1911
+ renderCtxTlFps = tl._fps;
1912
+ renderCtxTickTime = tlCildrenTickTime;
1913
+ renderCtxTickMode = tickMode;
1914
+ renderCtxMuteCallbacks = muteCallbacks;
1915
+ renderCtxInternalRender = internalRender;
1916
+ renderCtxChildrenHasRendered = 0;
1917
+ renderCtxChildrenHaveCompleted = true;
1918
+ forEachChildren(tl, tickRenderChild, tlIsRunningBackwards);
1919
+ tlChildrenHasRendered = renderCtxChildrenHasRendered;
1920
+ tlChildrenHaveCompleted = renderCtxChildrenHaveCompleted;
1719
1921
  // Renders on timeline are triggered by its children so it needs to be set after rendering the children
1720
1922
  if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
1721
-
1722
1923
  // Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
1723
1924
  if ((tlChildrenHaveCompleted || tlIsRunningBackwards) && tl._currentTime >= tl.duration) {
1724
1925
  // Make sure the paused flag is false in case it has been skipped in the render function
@@ -1772,59 +1973,78 @@
1772
1973
  /**
1773
1974
  * @template {Renderable} T
1774
1975
  * @param {T} renderable
1976
+ * @param {Boolean} [inlineStylesOnly]
1775
1977
  * @return {T}
1776
1978
  */
1777
- const cleanInlineStyles = renderable => {
1778
- // Allow cleanInlineStyles() to be called on timelines
1979
+ const revertValues = (renderable, inlineStylesOnly = false) => {
1980
+ // Allow revertValues() to be called on timelines
1779
1981
  if (renderable._hasChildren) {
1780
- forEachChildren(renderable, cleanInlineStyles, true);
1982
+ forEachChildren(renderable, (/** @type {Renderable} */child) => revertValues(child, inlineStylesOnly), true);
1781
1983
  } else {
1782
1984
  const animation = /** @type {JSAnimation} */(renderable);
1783
1985
  animation.pause();
1784
1986
  forEachChildren(animation, (/** @type {Tween} */tween) => {
1785
1987
  const tweenProperty = tween.property;
1786
1988
  const tweenTarget = tween.target;
1787
- if (tweenTarget[isDomSymbol]) {
1788
- const targetStyle = /** @type {DOMTarget} */(tweenTarget).style;
1789
- const originalInlinedValue = tween._inlineValue;
1790
- const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1791
- if (tween._tweenType === tweenTypes.TRANSFORM) {
1792
- const cachedTransforms = tweenTarget[transformsSymbol];
1793
- if (tweenHadNoInlineValue) {
1794
- delete cachedTransforms[tweenProperty];
1795
- } else {
1796
- cachedTransforms[tweenProperty] = originalInlinedValue;
1797
- }
1798
- if (tween._renderTransforms) {
1799
- if (!Object.keys(cachedTransforms).length) {
1800
- targetStyle.removeProperty('transform');
1989
+ const tweenType = tween._tweenType;
1990
+ const originalInlinedValue = tween._inlineValue;
1991
+ const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1992
+ if (tweenType === tweenTypes.OBJECT) {
1993
+ if (!inlineStylesOnly && !tweenHadNoInlineValue) {
1994
+ tweenTarget[tweenProperty] = originalInlinedValue;
1995
+ }
1996
+ } else if (tweenTarget[isDomSymbol]) {
1997
+ if (tweenType === tweenTypes.ATTRIBUTE) {
1998
+ if (!inlineStylesOnly) {
1999
+ if (tweenHadNoInlineValue) {
2000
+ /** @type {DOMTarget} */(tweenTarget).removeAttribute(tweenProperty);
1801
2001
  } else {
1802
- let str = emptyString;
1803
- for (let key in cachedTransforms) {
1804
- str += transformsFragmentStrings[key] + cachedTransforms[key] + ') ';
1805
- }
1806
- targetStyle.transform = str;
2002
+ /** @type {DOMTarget} */(tweenTarget).setAttribute(tweenProperty, /** @type {String} */(originalInlinedValue));
1807
2003
  }
1808
2004
  }
1809
2005
  } else {
1810
- if (tweenHadNoInlineValue) {
1811
- targetStyle.removeProperty(toLowerCase(tweenProperty));
2006
+ const targetStyle = /** @type {DOMTarget} */(tweenTarget).style;
2007
+ if (tweenType === tweenTypes.TRANSFORM) {
2008
+ const cachedTransforms = tweenTarget[transformsSymbol];
2009
+ if (tweenHadNoInlineValue) {
2010
+ delete cachedTransforms[tweenProperty];
2011
+ } else {
2012
+ cachedTransforms[tweenProperty] = originalInlinedValue;
2013
+ }
2014
+ if (tween._renderTransforms) {
2015
+ if (!Object.keys(cachedTransforms).length) {
2016
+ targetStyle.removeProperty('transform');
2017
+ } else {
2018
+ targetStyle.transform = buildTransformString(cachedTransforms);
2019
+ }
2020
+ }
1812
2021
  } else {
1813
- targetStyle[tweenProperty] = originalInlinedValue;
2022
+ if (tweenHadNoInlineValue) {
2023
+ targetStyle.removeProperty(toLowerCase(tweenProperty));
2024
+ } else {
2025
+ targetStyle[tweenProperty] = originalInlinedValue;
2026
+ }
1814
2027
  }
1815
2028
  }
1816
- if (animation._tail === tween) {
1817
- animation.targets.forEach(t => {
1818
- if (t.getAttribute && t.getAttribute('style') === emptyString) {
1819
- t.removeAttribute('style');
1820
- } });
1821
- }
2029
+ }
2030
+ if (tweenTarget[isDomSymbol] && animation._tail === tween) {
2031
+ animation.targets.forEach(t => {
2032
+ if (t.getAttribute && t.getAttribute('style') === emptyString) {
2033
+ t.removeAttribute('style');
2034
+ } });
1822
2035
  }
1823
2036
  });
1824
2037
  }
1825
2038
  return renderable;
1826
2039
  };
1827
2040
 
2041
+ /**
2042
+ * @template {Renderable} T
2043
+ * @param {T} renderable
2044
+ * @return {T}
2045
+ */
2046
+ const cleanInlineStyles = renderable => revertValues(renderable, true);
2047
+
1828
2048
 
1829
2049
 
1830
2050
  /*
@@ -2509,6 +2729,9 @@
2509
2729
 
2510
2730
  let timerId = 0;
2511
2731
 
2732
+ /** @param {Timer} prev @param {Timer} child */
2733
+ const sortByPriority = (prev, child) => prev._priority > child._priority;
2734
+
2512
2735
  /**
2513
2736
  * Base class used to create Timers, Animations and Timelines
2514
2737
  */
@@ -2535,6 +2758,7 @@
2535
2758
  autoplay,
2536
2759
  frameRate,
2537
2760
  playbackRate,
2761
+ priority,
2538
2762
  onComplete,
2539
2763
  onLoop,
2540
2764
  onPause,
@@ -2556,16 +2780,6 @@
2556
2780
  /** @type {Number} */(timerLoop) < 0 ? Infinity :
2557
2781
  /** @type {Number} */(timerLoop) + 1;
2558
2782
 
2559
- if (devTools) {
2560
- const isInfinite = timerIterationCount === Infinity;
2561
- const registered = devTools.register(this, parameters, isInfinite);
2562
- if (registered && isInfinite) {
2563
- const minIterations = alternate ? 2 : 1;
2564
- const iterations = parent ? devTools.maxNestedInfiniteLoops : devTools.maxInfiniteLoops;
2565
- timerIterationCount = Math.max(iterations, minIterations);
2566
- }
2567
- }
2568
-
2569
2783
  let offsetPosition = 0;
2570
2784
 
2571
2785
  if (parent) {
@@ -2649,6 +2863,8 @@
2649
2863
  this._fps = setValue(frameRate, timerDefaults.frameRate);
2650
2864
  /** @type {Number} */
2651
2865
  this._speed = setValue(playbackRate, timerDefaults.playbackRate);
2866
+ /** @type {Number} */
2867
+ this._priority = +setValue(priority, 1);
2652
2868
  }
2653
2869
 
2654
2870
  get cancelled() {
@@ -2793,7 +3009,7 @@
2793
3009
  tick(this, minValue, 0, 0, tickModes.FORCE);
2794
3010
  } else {
2795
3011
  if (!this._running) {
2796
- addChild(engine, this);
3012
+ addChild(engine, this, sortByPriority);
2797
3013
  engine._hasChildren = true;
2798
3014
  this._running = true;
2799
3015
  }
@@ -3388,7 +3604,7 @@
3388
3604
  * @param {Number} [parentPosition]
3389
3605
  * @param {Boolean} [fastSet=false]
3390
3606
  * @param {Number} [index=0]
3391
- * @param {Number} [length=0]
3607
+ * @param {TargetsArray} [allTargets]
3392
3608
  */
3393
3609
  constructor(
3394
3610
  targets,
@@ -3397,7 +3613,7 @@
3397
3613
  parentPosition,
3398
3614
  fastSet = false,
3399
3615
  index = 0,
3400
- length = 0
3616
+ allTargets
3401
3617
  ) {
3402
3618
 
3403
3619
  super(/** @type {TimerParams & AnimationParams} */(parameters), parent, parentPosition);
@@ -3448,7 +3664,7 @@
3448
3664
 
3449
3665
  const target = parsedTargets[targetIndex];
3450
3666
  const ti = index || targetIndex;
3451
- const tl = length || targetsLength;
3667
+ const tl = allTargets || parsedTargets;
3452
3668
 
3453
3669
  let lastTransformGroupIndex = NaN;
3454
3670
  let lastTransformGroupLength = NaN;
@@ -3524,7 +3740,14 @@
3524
3740
  toFunctionStore.func = null;
3525
3741
  fromFunctionStore.func = null;
3526
3742
 
3527
- const computedToValue = getFunctionValue(key.to, target, ti, tl, toFunctionStore);
3743
+ const computedComposition = getFunctionValue(setValue(key.composition, tComposition), target, ti, tl, null, null);
3744
+ const tweenComposition = isNum(computedComposition) ? computedComposition : compositionTypes[computedComposition];
3745
+ if (!siblings && tweenComposition !== compositionTypes.none) siblings = getTweenSiblings(target, propName);
3746
+ // Timelines pass the last sibling tween if it belongs to the same timeline
3747
+ // Standalone animations only pass prevTween when the property has multiple keyframes
3748
+ const tailTween = siblings ? siblings._tail : null;
3749
+ const prevSiblingTween = parent && tailTween && tailTween.parent.parent === parent ? tailTween : prevTween;
3750
+ const computedToValue = getFunctionValue(key.to, target, ti, tl, toFunctionStore, prevSiblingTween);
3528
3751
 
3529
3752
  let tweenToValue;
3530
3753
  // Allows function based values to return an object syntax value ({to: v})
@@ -3534,20 +3757,18 @@
3534
3757
  } else {
3535
3758
  tweenToValue = computedToValue;
3536
3759
  }
3537
- const tweenFromValue = getFunctionValue(key.from, target, ti, tl);
3760
+ const tweenFromValue = getFunctionValue(key.from, target, ti, tl, null, prevSiblingTween);
3538
3761
  const easeToParse = key.ease || tEasing;
3539
3762
 
3540
- const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl);
3763
+ const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl, null, prevSiblingTween);
3541
3764
  const keyEasing = isFnc(easeFunctionResult) || isStr(easeFunctionResult) ? easeFunctionResult : easeToParse;
3542
3765
 
3543
3766
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
3544
3767
  const tweenEasing = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
3545
3768
  // Calculate default individual keyframe duration by dividing the tl of keyframes
3546
- const tweenDuration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(setValue(key.duration, (l > 1 ? getFunctionValue(tDuration, target, ti, tl) / l : tDuration)), target, ti, tl);
3769
+ const tweenDuration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(setValue(key.duration, (l > 1 ? getFunctionValue(tDuration, target, ti, tl, null, prevSiblingTween) / l : tDuration)), target, ti, tl, null, prevSiblingTween);
3547
3770
  // Default delay value should only be applied to the first tween
3548
- const tweenDelay = getFunctionValue(setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl);
3549
- const computedComposition = getFunctionValue(setValue(key.composition, tComposition), target, ti, tl);
3550
- const tweenComposition = isNum(computedComposition) ? computedComposition : compositionTypes[computedComposition];
3771
+ const tweenDelay = getFunctionValue(setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl, null, prevSiblingTween);
3551
3772
  // Modifiers are treated differently and don't accept function based value to prevent having to pass a function wrapper
3552
3773
  const tweenModifier = key.modifier || tModifier;
3553
3774
  const hasFromvalue = !isUnd(tweenFromValue);
@@ -3564,7 +3785,6 @@
3564
3785
  let prevSibling = prevTween;
3565
3786
 
3566
3787
  if (tweenComposition !== compositionTypes.none) {
3567
- if (!siblings) siblings = getTweenSiblings(target, propName);
3568
3788
  let nextSibling = siblings._head;
3569
3789
  // Iterate trough all the next siblings until we find a sibling with an equal or inferior start time
3570
3790
  while (nextSibling && !nextSibling._isOverridden && nextSibling._absoluteStartTime <= absoluteStartTime) {
@@ -3583,8 +3803,8 @@
3583
3803
 
3584
3804
  // Decompose values
3585
3805
  if (isFromToValue) {
3586
- decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore) : tweenFromValue, fromTargetObject);
3587
- decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore) : tweenToValue, toTargetObject);
3806
+ decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore, prevSiblingTween) : tweenFromValue, fromTargetObject);
3807
+ decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore, prevSiblingTween) : tweenToValue, toTargetObject);
3588
3808
  // Needed to force an inline style registration
3589
3809
  const originalValue = getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore);
3590
3810
  if (fromTargetObject.t === valueTypes.NUMBER) {
@@ -3738,6 +3958,18 @@
3738
3958
  composeTween(tween, siblings);
3739
3959
  }
3740
3960
 
3961
+ // Pre-compute the tween end value for function-based value chaining (ie morphTo / scrambleText in keyframe arrays and timelines)
3962
+ const vt = tween._valueType;
3963
+ if (vt === valueTypes.COMPLEX) {
3964
+ tween._value = composeComplexValue(tween, 1, -1);
3965
+ } else if (vt === valueTypes.COLOR) {
3966
+ tween._value = composeColorValue(tween, 1, -1);
3967
+ } else if (vt === valueTypes.UNIT) {
3968
+ tween._value = `${tweenModifier(tween._toNumber)}${tween._unit}`;
3969
+ } else {
3970
+ tween._value = tweenModifier(tween._toNumber);
3971
+ }
3972
+
3741
3973
  if (isNaN(firstTweenChangeStartTime)) {
3742
3974
  firstTweenChangeStartTime = tween._startTime;
3743
3975
  }
@@ -3895,7 +4127,7 @@
3895
4127
  */
3896
4128
  revert() {
3897
4129
  super.revert();
3898
- return cleanInlineStyles(this);
4130
+ return revertValues(this);
3899
4131
  }
3900
4132
 
3901
4133
  /**
@@ -3916,171 +4148,13 @@
3916
4148
  * @param {TargetsParam} targets
3917
4149
  * @param {AnimationParams} parameters
3918
4150
  * @return {JSAnimation}
3919
- */
3920
- const animate = (targets, parameters) => new JSAnimation(targets, parameters, null, 0, false).init();
3921
-
3922
-
3923
-
3924
-
3925
-
3926
- const WAAPIAnimationsLookups = {
3927
- _head: null,
3928
- _tail: null,
3929
- };
3930
-
3931
- /**
3932
- * @param {DOMTarget} $el
3933
- * @param {String} [property]
3934
- * @param {WAAPIAnimation} [parent]
3935
- * @return {globalThis.Animation}
3936
- */
3937
- const removeWAAPIAnimation = ($el, property, parent) => {
3938
- let nextLookup = WAAPIAnimationsLookups._head;
3939
- let anim;
3940
- while (nextLookup) {
3941
- const next = nextLookup._next;
3942
- const matchTarget = nextLookup.$el === $el;
3943
- const matchProperty = !property || nextLookup.property === property;
3944
- const matchParent = !parent || nextLookup.parent === parent;
3945
- if (matchTarget && matchProperty && matchParent) {
3946
- anim = nextLookup.animation;
3947
- try { anim.commitStyles(); } catch {} anim.cancel();
3948
- removeChild(WAAPIAnimationsLookups, nextLookup);
3949
- const lookupParent = nextLookup.parent;
3950
- if (lookupParent) {
3951
- lookupParent._completed++;
3952
- if (lookupParent.animations.length === lookupParent._completed) {
3953
- lookupParent.completed = true;
3954
- lookupParent.paused = true;
3955
- if (!lookupParent.muteCallbacks) {
3956
- lookupParent.onComplete(lookupParent);
3957
- lookupParent._resolve(lookupParent);
3958
- }
3959
- }
3960
- }
3961
- }
3962
- nextLookup = next;
3963
- }
3964
- return anim;
3965
- };
3966
-
3967
- /**
3968
- * @param {WAAPIAnimation} parent
3969
- * @param {DOMTarget} $el
3970
- * @param {String} property
3971
- * @param {PropertyIndexedKeyframes} keyframes
3972
- * @param {KeyframeAnimationOptions} params
3973
- * @retun {globalThis.Animation}
3974
- */
3975
- const addWAAPIAnimation = (parent, $el, property, keyframes, params) => {
3976
- const animation = $el.animate(keyframes, params);
3977
- const animTotalDuration = params.delay + (+params.duration * params.iterations);
3978
- animation.playbackRate = parent._speed;
3979
- if (parent.paused) animation.pause();
3980
- if (parent.duration < animTotalDuration) {
3981
- parent.duration = animTotalDuration;
3982
- parent.controlAnimation = animation;
3983
- }
3984
- parent.animations.push(animation);
3985
- removeWAAPIAnimation($el, property);
3986
- addChild(WAAPIAnimationsLookups, { parent, animation, $el, property, _next: null, _prev: null });
3987
- const handleRemove = () => removeWAAPIAnimation($el, property, parent);
3988
- animation.oncancel = handleRemove;
3989
- animation.onremove = handleRemove;
3990
- if (!parent.persist) {
3991
- animation.onfinish = handleRemove;
3992
- }
3993
- return animation;
3994
- };
3995
-
3996
-
3997
-
3998
-
3999
-
4000
- /**
4001
- * @overload
4002
- * @param {DOMTargetSelector} targetSelector
4003
- * @param {String} propName
4004
- * @return {String}
4005
- *
4006
- * @overload
4007
- * @param {JSTargetsParam} targetSelector
4008
- * @param {String} propName
4009
- * @return {Number|String}
4010
- *
4011
- * @overload
4012
- * @param {DOMTargetsParam} targetSelector
4013
- * @param {String} propName
4014
- * @param {String} unit
4015
- * @return {String}
4016
- *
4017
- * @overload
4018
- * @param {TargetsParam} targetSelector
4019
- * @param {String} propName
4020
- * @param {Boolean} unit
4021
- * @return {Number}
4022
- *
4023
- * @param {TargetsParam} targetSelector
4024
- * @param {String} propName
4025
- * @param {String|Boolean} [unit]
4026
- */
4027
- function get(targetSelector, propName, unit) {
4028
- const targets = registerTargets(targetSelector);
4029
- if (!targets.length) return;
4030
- const [ target ] = targets;
4031
- const tweenType = getTweenType(target, propName);
4032
- const normalizePropName = sanitizePropertyName(propName, target, tweenType);
4033
- let originalValue = getOriginalAnimatableValue(target, normalizePropName);
4034
- if (isUnd(unit)) {
4035
- return originalValue;
4036
- } else {
4037
- decomposeRawValue(originalValue, decomposedOriginalValue);
4038
- if (decomposedOriginalValue.t === valueTypes.NUMBER || decomposedOriginalValue.t === valueTypes.UNIT) {
4039
- if (unit === false) {
4040
- return decomposedOriginalValue.n;
4041
- } else {
4042
- const convertedValue = convertValueUnit(/** @type {DOMTarget} */(target), decomposedOriginalValue, /** @type {String} */(unit), false);
4043
- return `${round$1(convertedValue.n, globals.precision)}${convertedValue.u}`;
4044
- }
4045
- }
4046
- }
4047
- }
4048
-
4049
- /**
4050
- * @param {TargetsParam} targets
4051
- * @param {AnimationParams} parameters
4052
- * @return {JSAnimation}
4053
- */
4054
- const set = (targets, parameters) => {
4055
- if (isUnd(parameters)) return;
4056
- parameters.duration = minValue;
4057
- // Do not overrides currently active tweens by default
4058
- parameters.composition = setValue(parameters.composition, compositionTypes.none);
4059
- // Skip init() and force rendering by playing the animation
4060
- return new JSAnimation(targets, parameters, null, 0, true).resume();
4061
- };
4062
-
4063
- /**
4064
- * @param {TargetsParam} targets
4065
- * @param {Renderable|WAAPIAnimation} [renderable]
4066
- * @param {String} [propertyName]
4067
- * @return {TargetsArray}
4068
- */
4069
- const remove = (targets, renderable, propertyName) => {
4070
- const targetsArray = parseTargets(targets);
4071
- for (let i = 0, l = targetsArray.length; i < l; i++) {
4072
- removeWAAPIAnimation(
4073
- /** @type {DOMTarget} */(targetsArray[i]),
4074
- propertyName,
4075
- renderable && /** @type {WAAPIAnimation} */(renderable).controlAnimation && /** @type {WAAPIAnimation} */(renderable),
4076
- );
4151
+ */
4152
+ const animate = (targets, parameters) => {
4153
+ if (globals.editor) {
4154
+ return globals.editor.addAnimation(targets, parameters);
4155
+ } else {
4156
+ return new JSAnimation(targets, parameters, null, 0, false).init();
4077
4157
  }
4078
- removeTargetsFromRenderable(
4079
- targetsArray,
4080
- /** @type {Renderable} */(renderable),
4081
- propertyName
4082
- );
4083
- return targetsArray;
4084
4158
  };
4085
4159
 
4086
4160
 
@@ -4136,6 +4210,8 @@
4136
4210
 
4137
4211
 
4138
4212
 
4213
+
4214
+
4139
4215
  /**
4140
4216
  * @param {Timeline} tl
4141
4217
  * @return {Number}
@@ -4157,7 +4233,7 @@
4157
4233
  * @param {Number} timePosition
4158
4234
  * @param {TargetsParam} targets
4159
4235
  * @param {Number} [index]
4160
- * @param {Number} [length]
4236
+ * @param {TargetsArray} [allTargets]
4161
4237
  * @return {Timeline}
4162
4238
  *
4163
4239
  * @param {TimerParams|AnimationParams} childParams
@@ -4165,15 +4241,15 @@
4165
4241
  * @param {Number} timePosition
4166
4242
  * @param {TargetsParam} [targets]
4167
4243
  * @param {Number} [index]
4168
- * @param {Number} [length]
4244
+ * @param {TargetsArray} [allTargets]
4169
4245
  */
4170
- function addTlChild(childParams, tl, timePosition, targets, index, length) {
4246
+ function addTlChild(childParams, tl, timePosition, targets, index, allTargets) {
4171
4247
  const isSetter = isNum(childParams.duration) && /** @type {Number} */(childParams.duration) <= minValue;
4172
4248
  // Offset the tl position with -minValue for 0 duration animations or .set() calls in order to align their end value with the defined position
4173
4249
  const adjustedPosition = isSetter ? timePosition - minValue : timePosition;
4174
4250
  if (tl.composition) tick(tl, adjustedPosition, 1, 1, tickModes.AUTO);
4175
4251
  const tlChild = targets ?
4176
- new JSAnimation(targets,/** @type {AnimationParams} */(childParams), tl, adjustedPosition, false, index, length) :
4252
+ new JSAnimation(targets,/** @type {AnimationParams} */(childParams), tl, adjustedPosition, false, index, allTargets) :
4177
4253
  new Timer(/** @type {TimerParams} */(childParams), tl, adjustedPosition);
4178
4254
  if (tl.composition) tlChild.init(true);
4179
4255
  // TODO: Might be better to insert at a position relative to startTime?
@@ -4221,7 +4297,7 @@
4221
4297
  * @overload
4222
4298
  * @param {TargetsParam} a1
4223
4299
  * @param {AnimationParams} a2
4224
- * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4300
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [a3]
4225
4301
  * @return {this}
4226
4302
  *
4227
4303
  * @overload
@@ -4231,7 +4307,7 @@
4231
4307
  *
4232
4308
  * @param {TargetsParam|TimerParams} a1
4233
4309
  * @param {TimelinePosition|AnimationParams} a2
4234
- * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4310
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [a3]
4235
4311
  */
4236
4312
  add(a1, a2, a3) {
4237
4313
  const isAnim = isObj(a2);
@@ -4240,9 +4316,11 @@
4240
4316
  this._hasChildren = true;
4241
4317
  if (isAnim) {
4242
4318
  const childParams = /** @type {AnimationParams} */(a2);
4243
- // Check for function for children stagger positions
4244
- if (isFnc(a3)) {
4245
- const staggeredPosition = a3;
4319
+ const editorHook = globals.editor && globals.editor.addTimelineChild;
4320
+ const isStaggerType = a3 && /** @type {TweakRegister} */(a3).type === 'Stagger' && globals.editor;
4321
+ // Check for function or Stagger type children positions
4322
+ const staggeredPosition = isFnc(a3) ? a3 : null;
4323
+ if (staggeredPosition || isStaggerType) {
4246
4324
  const parsedTargetsArray = parseTargets(/** @type {TargetsParam} */(a1));
4247
4325
  // Store initial duration before adding new children that will change the duration
4248
4326
  const tlDuration = this.duration;
@@ -4253,28 +4331,36 @@
4253
4331
  let i = 0;
4254
4332
  /** @type {Number} */
4255
4333
  const parsedLength = (parsedTargetsArray.length);
4334
+ // Call editor hook once for the entire stagger group instead of per target
4335
+ const resolvedParams = editorHook ? editorHook(/** @type {TargetsParam} */(a1), childParams, this.id, a3, parsedLength) : null;
4336
+ // Resolve stagger AFTER editor hook so tweaked position value (a3.defaultValue) is used
4337
+ const staggerFn = staggeredPosition || globals.editor.resolveStagger(/** @type {TweakRegister} */(a3).defaultValue);
4256
4338
  parsedTargetsArray.forEach((/** @type {Target} */target) => {
4257
4339
  // Create a new parameter object for each staggered children
4258
- const staggeredChildParams = { ...childParams };
4340
+ const staggeredChildParams = { ...(resolvedParams || childParams) };
4259
4341
  // Reset the duration of the timeline iteration before each stagger to prevent wrong start value calculation
4260
4342
  this.duration = tlDuration;
4261
4343
  this.iterationDuration = tlIterationDuration;
4262
4344
  if (!isUnd(id)) staggeredChildParams.id = id + '-' + i;
4345
+ const staggeredTimePosition = parseTimelinePosition(this, staggerFn(target, i, parsedTargetsArray, null, this));
4263
4346
  addTlChild(
4264
4347
  staggeredChildParams,
4265
4348
  this,
4266
- parseTimelinePosition(this, staggeredPosition(target, i, parsedLength, this)),
4349
+ staggeredTimePosition,
4267
4350
  target,
4268
4351
  i,
4269
- parsedLength
4352
+ parsedTargetsArray,
4270
4353
  );
4271
4354
  i++;
4272
4355
  });
4273
4356
  } else {
4357
+ // Call editor hook before resolving position so tweaked values are applied
4358
+ const resolvedChildParams = editorHook ? editorHook(/** @type {TargetsParam} */(a1), childParams, this.id, a3) : childParams;
4359
+ const resolvedPosition = a3 && /** @type {*} */(a3).type ? /** @type {*} */(a3).defaultValue : a3;
4274
4360
  addTlChild(
4275
- childParams,
4361
+ resolvedChildParams,
4276
4362
  this,
4277
- parseTimelinePosition(this, a3),
4363
+ parseTimelinePosition(this, resolvedPosition),
4278
4364
  /** @type {TargetsParam} */(a1),
4279
4365
  );
4280
4366
  }
@@ -4396,7 +4482,7 @@
4396
4482
  revert() {
4397
4483
  super.revert();
4398
4484
  forEachChildren(this, (/** @type {JSAnimation|Timer} */child) => child.revert, true);
4399
- return cleanInlineStyles(this);
4485
+ return revertValues(this);
4400
4486
  }
4401
4487
 
4402
4488
  /**
@@ -4416,7 +4502,12 @@
4416
4502
  * @param {TimelineParams} [parameters]
4417
4503
  * @return {Timeline}
4418
4504
  */
4419
- const createTimeline = parameters => new Timeline(parameters).init();
4505
+ const createTimeline = parameters => {
4506
+ if (globals.editor) {
4507
+ return /** @type {Timeline} */(/** @type {unknown} */(globals.editor.addTimeline(parameters)));
4508
+ }
4509
+ return new Timeline(parameters).init();
4510
+ };
4420
4511
 
4421
4512
 
4422
4513
 
@@ -4863,6 +4954,170 @@
4863
4954
 
4864
4955
 
4865
4956
 
4957
+ const WAAPIAnimationsLookups = {
4958
+ _head: null,
4959
+ _tail: null,
4960
+ };
4961
+
4962
+ /**
4963
+ * @param {DOMTarget} $el
4964
+ * @param {String} [property]
4965
+ * @param {WAAPIAnimation} [parent]
4966
+ * @return {globalThis.Animation}
4967
+ */
4968
+ const removeWAAPIAnimation = ($el, property, parent) => {
4969
+ let nextLookup = WAAPIAnimationsLookups._head;
4970
+ let anim;
4971
+ while (nextLookup) {
4972
+ const next = nextLookup._next;
4973
+ const matchTarget = nextLookup.$el === $el;
4974
+ const matchProperty = !property || nextLookup.property === property;
4975
+ const matchParent = !parent || nextLookup.parent === parent;
4976
+ if (matchTarget && matchProperty && matchParent) {
4977
+ anim = nextLookup.animation;
4978
+ try { anim.commitStyles(); } catch {} anim.cancel();
4979
+ removeChild(WAAPIAnimationsLookups, nextLookup);
4980
+ const lookupParent = nextLookup.parent;
4981
+ if (lookupParent) {
4982
+ lookupParent._completed++;
4983
+ if (lookupParent.animations.length === lookupParent._completed) {
4984
+ lookupParent.completed = true;
4985
+ lookupParent.paused = true;
4986
+ if (!lookupParent.muteCallbacks) {
4987
+ lookupParent.onComplete(lookupParent);
4988
+ lookupParent._resolve(lookupParent);
4989
+ }
4990
+ }
4991
+ }
4992
+ }
4993
+ nextLookup = next;
4994
+ }
4995
+ return anim;
4996
+ };
4997
+
4998
+ /**
4999
+ * @param {WAAPIAnimation} parent
5000
+ * @param {DOMTarget} $el
5001
+ * @param {String} property
5002
+ * @param {PropertyIndexedKeyframes} keyframes
5003
+ * @param {KeyframeAnimationOptions} params
5004
+ * @retun {globalThis.Animation}
5005
+ */
5006
+ const addWAAPIAnimation = (parent, $el, property, keyframes, params) => {
5007
+ const animation = $el.animate(keyframes, params);
5008
+ const animTotalDuration = params.delay + (+params.duration * params.iterations);
5009
+ animation.playbackRate = parent._speed;
5010
+ if (parent.paused) animation.pause();
5011
+ if (parent.duration < animTotalDuration) {
5012
+ parent.duration = animTotalDuration;
5013
+ parent.controlAnimation = animation;
5014
+ }
5015
+ parent.animations.push(animation);
5016
+ removeWAAPIAnimation($el, property);
5017
+ addChild(WAAPIAnimationsLookups, { parent, animation, $el, property, _next: null, _prev: null });
5018
+ const handleRemove = () => removeWAAPIAnimation($el, property, parent);
5019
+ animation.oncancel = handleRemove;
5020
+ animation.onremove = handleRemove;
5021
+ if (!parent.persist) {
5022
+ animation.onfinish = handleRemove;
5023
+ }
5024
+ return animation;
5025
+ };
5026
+
5027
+
5028
+
5029
+
5030
+
5031
+ /**
5032
+ * @overload
5033
+ * @param {DOMTargetSelector} targetSelector
5034
+ * @param {String} propName
5035
+ * @return {String}
5036
+ *
5037
+ * @overload
5038
+ * @param {JSTargetsParam} targetSelector
5039
+ * @param {String} propName
5040
+ * @return {Number|String}
5041
+ *
5042
+ * @overload
5043
+ * @param {DOMTargetsParam} targetSelector
5044
+ * @param {String} propName
5045
+ * @param {String} unit
5046
+ * @return {String}
5047
+ *
5048
+ * @overload
5049
+ * @param {TargetsParam} targetSelector
5050
+ * @param {String} propName
5051
+ * @param {Boolean} unit
5052
+ * @return {Number}
5053
+ *
5054
+ * @param {TargetsParam} targetSelector
5055
+ * @param {String} propName
5056
+ * @param {String|Boolean} [unit]
5057
+ */
5058
+ function get(targetSelector, propName, unit) {
5059
+ const targets = registerTargets(targetSelector);
5060
+ if (!targets.length) return;
5061
+ const [ target ] = targets;
5062
+ const tweenType = getTweenType(target, propName);
5063
+ const normalizePropName = sanitizePropertyName(propName, target, tweenType);
5064
+ let originalValue = getOriginalAnimatableValue(target, normalizePropName);
5065
+ if (isUnd(unit)) {
5066
+ return originalValue;
5067
+ } else {
5068
+ decomposeRawValue(originalValue, decomposedOriginalValue);
5069
+ if (decomposedOriginalValue.t === valueTypes.NUMBER || decomposedOriginalValue.t === valueTypes.UNIT) {
5070
+ if (unit === false) {
5071
+ return decomposedOriginalValue.n;
5072
+ } else {
5073
+ const convertedValue = convertValueUnit(/** @type {DOMTarget} */(target), decomposedOriginalValue, /** @type {String} */(unit), false);
5074
+ return `${round$1(convertedValue.n, globals.precision)}${convertedValue.u}`;
5075
+ }
5076
+ }
5077
+ }
5078
+ }
5079
+
5080
+ /**
5081
+ * @param {TargetsParam} targets
5082
+ * @param {AnimationParams} parameters
5083
+ * @return {JSAnimation}
5084
+ */
5085
+ const set = (targets, parameters) => {
5086
+ if (isUnd(parameters)) return;
5087
+ parameters.duration = minValue;
5088
+ // Do not overrides currently active tweens by default
5089
+ parameters.composition = setValue(parameters.composition, compositionTypes.none);
5090
+ // Skip init() and force rendering by playing the animation
5091
+ return new JSAnimation(targets, parameters, null, 0, true).resume();
5092
+ };
5093
+
5094
+ /**
5095
+ * @param {TargetsParam} targets
5096
+ * @param {Renderable|WAAPIAnimation} [renderable]
5097
+ * @param {String} [propertyName]
5098
+ * @return {TargetsArray}
5099
+ */
5100
+ const remove = (targets, renderable, propertyName) => {
5101
+ const targetsArray = parseTargets(targets);
5102
+ for (let i = 0, l = targetsArray.length; i < l; i++) {
5103
+ removeWAAPIAnimation(
5104
+ /** @type {DOMTarget} */(targetsArray[i]),
5105
+ propertyName,
5106
+ renderable && /** @type {WAAPIAnimation} */(renderable).controlAnimation && /** @type {WAAPIAnimation} */(renderable),
5107
+ );
5108
+ }
5109
+ removeTargetsFromRenderable(
5110
+ targetsArray,
5111
+ /** @type {Renderable} */(renderable),
5112
+ propertyName
5113
+ );
5114
+ return targetsArray;
5115
+ };
5116
+
5117
+
5118
+
5119
+
5120
+
4866
5121
  /**
4867
5122
  * @param {Event} e
4868
5123
  */
@@ -6065,19 +6320,20 @@
6065
6320
  };
6066
6321
 
6067
6322
  /**
6068
- * @param {(...args: any[]) => Tickable | ((...args: any[]) => void)} constructor
6323
+ * @param {(...args: any[]) => Tickable | ((...args: any[]) => void) | void} constructor
6069
6324
  * @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
6070
6325
  */
6071
6326
  const keepTime = constructor => {
6072
6327
  /** @type {Tickable} */
6073
6328
  let tracked;
6074
6329
  return (...args) => {
6075
- let currentIteration, currentIterationProgress, reversed, alternate;
6330
+ let currentIteration, currentIterationProgress, reversed, alternate, startTime;
6076
6331
  if (tracked) {
6077
6332
  currentIteration = tracked.currentIteration;
6078
6333
  currentIterationProgress = tracked.iterationProgress;
6079
6334
  reversed = tracked.reversed;
6080
6335
  alternate = tracked._alternate;
6336
+ startTime = tracked._startTime;
6081
6337
  tracked.revert();
6082
6338
  }
6083
6339
  const cleanup = constructor(...args);
@@ -6085,6 +6341,7 @@
6085
6341
  if (!isUnd(currentIterationProgress)) {
6086
6342
  /** @type {Tickable} */(tracked).currentIteration = currentIteration;
6087
6343
  /** @type {Tickable} */(tracked).iterationProgress = (alternate ? !(currentIteration % 2) ? reversed : !reversed : reversed) ? 1 - currentIterationProgress : currentIterationProgress;
6344
+ /** @type {Tickable} */(tracked)._startTime = startTime;
6088
6345
  }
6089
6346
  return cleanup || noop;
6090
6347
  }
@@ -7430,12 +7687,12 @@
7430
7687
  * @param {WAAPIKeyframeValue} value
7431
7688
  * @param {DOMTarget} $el
7432
7689
  * @param {Number} i
7433
- * @param {Number} targetsLength
7690
+ * @param {DOMTargetsArray} parsedTargets
7434
7691
  * @return {String}
7435
7692
  */
7436
- const normalizeTweenValue = (propName, value, $el, i, targetsLength) => {
7693
+ const normalizeTweenValue = (propName, value, $el, i, parsedTargets) => {
7437
7694
  // Do not try to compute strings with getFunctionValue otherwise it will convert CSS variables
7438
- let v = isStr(value) ? value : getFunctionValue(/** @type {any} */(value), $el, i, targetsLength);
7695
+ let v = isStr(value) ? value : getFunctionValue(/** @type {any} */(value), $el, i, parsedTargets, null, null);
7439
7696
  if (!isNum(v)) return v;
7440
7697
  if (commonDefaultPXProperties.includes(propName) || stringStartsWith(propName, 'translate')) return `${v}px`;
7441
7698
  if (stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew')) return `${v}deg`;
@@ -7448,18 +7705,18 @@
7448
7705
  * @param {WAAPIKeyframeValue} from
7449
7706
  * @param {WAAPIKeyframeValue} to
7450
7707
  * @param {Number} i
7451
- * @param {Number} targetsLength
7708
+ * @param {DOMTargetsArray} parsedTargets
7452
7709
  * @return {WAAPITweenValue}
7453
7710
  */
7454
- const parseIndividualTweenValue = ($el, propName, from, to, i, targetsLength) => {
7711
+ const parseIndividualTweenValue = ($el, propName, from, to, i, parsedTargets) => {
7455
7712
  /** @type {WAAPITweenValue} */
7456
7713
  let tweenValue = '0';
7457
- const computedTo = !isUnd(to) ? normalizeTweenValue(propName, to, $el, i, targetsLength) : getComputedStyle($el)[propName];
7714
+ const computedTo = !isUnd(to) ? normalizeTweenValue(propName, to, $el, i, parsedTargets) : getComputedStyle($el)[propName];
7458
7715
  if (!isUnd(from)) {
7459
- const computedFrom = normalizeTweenValue(propName, from, $el, i, targetsLength);
7716
+ const computedFrom = normalizeTweenValue(propName, from, $el, i, parsedTargets);
7460
7717
  tweenValue = [computedFrom, computedTo];
7461
7718
  } else {
7462
- tweenValue = isArr(to) ? to.map((/** @type {any} */v) => normalizeTweenValue(propName, v, $el, i, targetsLength)) : computedTo;
7719
+ tweenValue = isArr(to) ? to.map((/** @type {any} */v) => normalizeTweenValue(propName, v, $el, i, parsedTargets)) : computedTo;
7463
7720
  }
7464
7721
  return tweenValue;
7465
7722
  };
@@ -7498,9 +7755,8 @@
7498
7755
  }
7499
7756
 
7500
7757
  const parsedTargets = registerTargets(targets);
7501
- const targetsLength = parsedTargets.length;
7502
7758
 
7503
- if (!targetsLength) {
7759
+ if (!parsedTargets.length) {
7504
7760
  console.warn(`No target found. Make sure the element you're trying to animate is accessible before creating your animation.`);
7505
7761
  }
7506
7762
 
@@ -7556,7 +7812,7 @@
7556
7812
 
7557
7813
  const easeToParse = setValue(params.ease, globals.defaults.ease);
7558
7814
 
7559
- const easeFunctionResult = getFunctionValue(easeToParse, $el, i, targetsLength);
7815
+ const easeFunctionResult = getFunctionValue(easeToParse, $el, i, parsedTargets, null, null);
7560
7816
  const keyEasing = isFnc(easeFunctionResult) || isStr(easeFunctionResult) ? easeFunctionResult : easeToParse;
7561
7817
 
7562
7818
  const spring = /** @type {Spring} */(easeToParse).ease && easeToParse;
@@ -7564,9 +7820,9 @@
7564
7820
  const easing = parseWAAPIEasing(keyEasing);
7565
7821
 
7566
7822
  /** @type {Number} */
7567
- const duration = (spring ? /** @type {Spring} */(spring).settlingDuration : getFunctionValue(setValue(params.duration, globals.defaults.duration), $el, i, targetsLength)) * timeScale;
7823
+ const duration = (spring ? /** @type {Spring} */(spring).settlingDuration : getFunctionValue(setValue(params.duration, globals.defaults.duration), $el, i, parsedTargets, null, null)) * timeScale;
7568
7824
  /** @type {Number} */
7569
- const delay = getFunctionValue(setValue(params.delay, globals.defaults.delay), $el, i, targetsLength) * timeScale;
7825
+ const delay = getFunctionValue(setValue(params.delay, globals.defaults.delay), $el, i, parsedTargets, null, null) * timeScale;
7570
7826
  /** @type {CompositeOperation} */
7571
7827
  const composite = /** @type {CompositeOperation} */(setValue(params.composition, 'replace'));
7572
7828
 
@@ -7592,19 +7848,19 @@
7592
7848
  const to = /** @type {WAAPITweenOptions} */(tweenOptions).to;
7593
7849
  const from = /** @type {WAAPITweenOptions} */(tweenOptions).from;
7594
7850
  /** @type {Number} */
7595
- tweenParams.duration = (tweenOptionsSpring ? /** @type {Spring} */(tweenOptionsSpring).settlingDuration : getFunctionValue(setValue(tweenOptions.duration, duration), $el, i, targetsLength)) * timeScale;
7851
+ tweenParams.duration = (tweenOptionsSpring ? /** @type {Spring} */(tweenOptionsSpring).settlingDuration : getFunctionValue(setValue(tweenOptions.duration, duration), $el, i, parsedTargets, null, null)) * timeScale;
7596
7852
  /** @type {Number} */
7597
- tweenParams.delay = getFunctionValue(setValue(tweenOptions.delay, delay), $el, i, targetsLength) * timeScale;
7853
+ tweenParams.delay = getFunctionValue(setValue(tweenOptions.delay, delay), $el, i, parsedTargets, null, null) * timeScale;
7598
7854
  /** @type {CompositeOperation} */
7599
7855
  tweenParams.composite = /** @type {CompositeOperation} */(setValue(tweenOptions.composition, composite));
7600
7856
  /** @type {String} */
7601
7857
  tweenParams.easing = parseWAAPIEasing(tweenOptionsEase);
7602
- parsedPropertyValue = parseIndividualTweenValue($el, name, from, to, i, targetsLength);
7858
+ parsedPropertyValue = parseIndividualTweenValue($el, name, from, to, i, parsedTargets);
7603
7859
  if (individualTransformProperty) {
7604
7860
  keyframes[`--${individualTransformProperty}`] = parsedPropertyValue;
7605
7861
  cachedTransforms[individualTransformProperty] = parsedPropertyValue;
7606
7862
  } else {
7607
- keyframes[name] = parseIndividualTweenValue($el, name, from, to, i, targetsLength);
7863
+ keyframes[name] = parseIndividualTweenValue($el, name, from, to, i, parsedTargets);
7608
7864
  }
7609
7865
  addWAAPIAnimation(this, $el, name, keyframes, tweenParams);
7610
7866
  if (!isUnd(from)) {
@@ -7617,8 +7873,8 @@
7617
7873
  }
7618
7874
  } else {
7619
7875
  parsedPropertyValue = isArr(propertyValue) ?
7620
- propertyValue.map((/** @type {any} */v) => normalizeTweenValue(name, v, $el, i, targetsLength)) :
7621
- normalizeTweenValue(name, /** @type {any} */(propertyValue), $el, i, targetsLength);
7876
+ propertyValue.map((/** @type {any} */v) => normalizeTweenValue(name, v, $el, i, parsedTargets)) :
7877
+ normalizeTweenValue(name, /** @type {any} */(propertyValue), $el, i, parsedTargets);
7622
7878
  if (individualTransformProperty) {
7623
7879
  keyframes[`--${individualTransformProperty}`] = parsedPropertyValue;
7624
7880
  cachedTransforms[individualTransformProperty] = parsedPropertyValue;
@@ -7851,6 +8107,7 @@
7851
8107
 
7852
8108
  /**
7853
8109
  * @typedef {Object} LayoutSpecificAnimationParams
8110
+ * @property {Number|String} [id]
7854
8111
  * @property {Number|FunctionValue} [delay]
7855
8112
  * @property {Number|FunctionValue} [duration]
7856
8113
  * @property {EasingParam|FunctionValue} [ease]
@@ -7893,7 +8150,7 @@
7893
8150
  * @property {String} id
7894
8151
  * @property {DOMTarget} $el
7895
8152
  * @property {Number} index
7896
- * @property {Number} total
8153
+ * @property {Array<DOMTarget>} targets
7897
8154
  * @property {Number} delay
7898
8155
  * @property {Number} duration
7899
8156
  * @property {EasingParam} ease
@@ -8037,7 +8294,7 @@
8037
8294
  node.$measure = $el;
8038
8295
  node.id = dataId;
8039
8296
  node.index = 0;
8040
- node.total = 1;
8297
+ node.targets = null;
8041
8298
  node.delay = 0;
8042
8299
  node.duration = 0;
8043
8300
  node.ease = null;
@@ -8223,12 +8480,12 @@
8223
8480
  * @param {LayoutAnimationTimingsParams} params
8224
8481
  */
8225
8482
  const updateNodeTimingParams = (node, params) => {
8226
- const easeFunctionResult = getFunctionValue(params.ease, node.$el, node.index, node.total);
8483
+ const easeFunctionResult = getFunctionValue(params.ease, node.$el, node.index, node.targets, null, null);
8227
8484
  const keyEasing = isFnc(easeFunctionResult) ? easeFunctionResult : params.ease;
8228
8485
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
8229
8486
  node.ease = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
8230
- node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.total);
8231
- node.delay = getFunctionValue(params.delay, node.$el, node.index, node.total);
8487
+ node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.targets, null, null);
8488
+ node.delay = getFunctionValue(params.delay, node.$el, node.index, node.targets, null, null);
8232
8489
  };
8233
8490
 
8234
8491
  /**
@@ -8601,10 +8858,12 @@
8601
8858
 
8602
8859
  const inRootNodeIds = new Set();
8603
8860
  // Update index and total for inital timing calculation
8604
- let index = 0, total = this.nodes.size;
8861
+ let index = 0;
8862
+ const allNodeTargets = [];
8863
+ this.nodes.forEach((node) => { allNodeTargets.push(node.$el); });
8605
8864
  this.nodes.forEach((node, id) => {
8606
8865
  node.index = index++;
8607
- node.total = total;
8866
+ node.targets = allNodeTargets;
8608
8867
  // Track ids of nodes that belong to the current root to filter detached matches
8609
8868
  if (node && node.measuredIsInsideRoot) {
8610
8869
  inRootNodeIds.add(id);
@@ -8716,8 +8975,8 @@
8716
8975
  this.params = params;
8717
8976
  /** @type {DOMTarget} */
8718
8977
  this.root = /** @type {DOMTarget} */(registerTargets(root)[0]);
8719
- /** @type {Number} */
8720
- this.id = layoutId++;
8978
+ /** @type {Number|String} */
8979
+ this.id = params.id || layoutId++;
8721
8980
  /** @type {LayoutChildrenParam} */
8722
8981
  this.children = params.children || '*';
8723
8982
  /** @type {Boolean} */
@@ -8840,7 +9099,9 @@
8840
9099
  duration: setValue(params.duration, this.params.duration),
8841
9100
  };
8842
9101
  /** @type {TimelineParams} */
8843
- const tlParams = {};
9102
+ const tlParams = {
9103
+ id: this.id
9104
+ };
8844
9105
  const onComplete = setValue(params.onComplete, this.params.onComplete);
8845
9106
  const onPause = setValue(params.onPause, this.params.onPause);
8846
9107
  for (let name in defaults) {
@@ -8854,7 +9115,8 @@
8854
9115
  }
8855
9116
  tlParams.onComplete = () => {
8856
9117
  const ap = /** @type {ScrollObserver} */(params.autoplay);
8857
- const isScrollControled = ap && ap.linked;
9118
+ const ed = globals.editor;
9119
+ const isScrollControled = (ap && ap.linked) || (ed && ed.showPanel);
8858
9120
  if (isScrollControled) {
8859
9121
  if (onComplete) onComplete(this.timeline);
8860
9122
  return;
@@ -9053,41 +9315,39 @@
9053
9315
  animatedParent = animatedParent.parentNode;
9054
9316
  }
9055
9317
 
9056
- const animatingTotal = animating.length;
9057
-
9058
9318
  // Root is always animated first in sync with the first child (animating.length is the total of children)
9059
9319
  if (node === rootNode) {
9060
9320
  node.index = 0;
9061
- node.total = animatingTotal;
9321
+ node.targets = animating;
9062
9322
  updateNodeTimingParams(node, animationTimings);
9063
9323
  } else if (node.isEntering) {
9064
9324
  node.index = animatedParent ? animatedParent.index : enteringIndex;
9065
- node.total = animatedParent ? animatingTotal : entering.length;
9325
+ node.targets = animatedParent ? animating : entering;
9066
9326
  updateNodeTimingParams(node, enterFromTimings);
9067
9327
  enteringIndex++;
9068
9328
  } else if (node.isLeaving) {
9069
9329
  node.index = animatedParent ? animatedParent.index : leavingIndex;
9070
- node.total = animatedParent ? animatingTotal : leaving.length;
9330
+ node.targets = animatedParent ? animating : leaving;
9071
9331
  leavingIndex++;
9072
9332
  updateNodeTimingParams(node, leaveToTimings);
9073
9333
  } else if (node.isTarget) {
9074
9334
  node.index = animatingIndex++;
9075
- node.total = animatingTotal;
9335
+ node.targets = animating;
9076
9336
  updateNodeTimingParams(node, animationTimings);
9077
9337
  } else {
9078
9338
  node.index = animatedParent ? animatedParent.index : 0;
9079
- node.total = animatingTotal;
9339
+ node.targets = animating;
9080
9340
  updateNodeTimingParams(node, swapAtTimings);
9081
9341
  }
9082
9342
 
9083
9343
  // Make sure the old state node has its inex and total values up to date for valid "from" function values calculation
9084
9344
  oldStateNode.index = node.index;
9085
- oldStateNode.total = node.total;
9345
+ oldStateNode.targets = node.targets;
9086
9346
 
9087
9347
  // Computes all values up front so we can check for changes and we don't have to re-compute them inside the animation props
9088
9348
  for (let prop in nodeProperties) {
9089
- nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.total);
9090
- oldStateNodeProperties[prop] = getFunctionValue(oldStateNodeProperties[prop], $el, oldStateNode.index, oldStateNode.total);
9349
+ nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.targets, null, null);
9350
+ oldStateNodeProperties[prop] = getFunctionValue(oldStateNodeProperties[prop], $el, oldStateNode.index, oldStateNode.targets, null, null);
9091
9351
  }
9092
9352
 
9093
9353
  // Use a 1px tolerance to detect dimensions changes to prevent width / height animations on barelly visible elements
@@ -9313,8 +9573,8 @@
9313
9573
  }
9314
9574
  $el.style.transform = oldState.getComputedValue($el, 'transform');
9315
9575
  if (animatedSwap.includes($el)) {
9316
- node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.total);
9317
- node.duration = getFunctionValue(swapAtParams.duration, $el, node.index, node.total);
9576
+ node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.targets, null, null);
9577
+ node.duration = getFunctionValue(swapAtParams.duration, $el, node.index, node.targets, null, null);
9318
9578
  }
9319
9579
  }
9320
9580
  this.transformAnimation = waapi.animate(transformed, {
@@ -9329,7 +9589,7 @@
9329
9589
  if (!animatedSwap.includes($el)) return newValue;
9330
9590
  const oldValue = oldState.getComputedValue($el, 'transform');
9331
9591
  const node = newState.getNode($el);
9332
- return [oldValue, getFunctionValue(swapAtProps.transform, $el, node.index, node.total), newValue]
9592
+ return [oldValue, getFunctionValue(swapAtProps.transform, $el, node.index, node.targets, null, null), newValue]
9333
9593
  },
9334
9594
  autoplay: false,
9335
9595
  // persist: true,
@@ -9386,10 +9646,13 @@
9386
9646
  const result = fn(...args);
9387
9647
  return new Proxy(noop, {
9388
9648
  apply: (_, __, [v]) => result(v),
9389
- get: (_, prop) => chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9390
- const nextResult = chainables[prop](...nextArgs);
9391
- return (/**@type {Number|String} */v) => nextResult(result(v));
9392
- })
9649
+ get: (_, prop) => {
9650
+ if (!chainables[prop]) return undefined;
9651
+ return chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9652
+ const nextResult = chainables[prop](...nextArgs);
9653
+ return (/**@type {Number|String} */v) => nextResult(result(v));
9654
+ })
9655
+ }
9393
9656
  });
9394
9657
  }
9395
9658
  };
@@ -9600,24 +9863,28 @@
9600
9863
  * @param {StaggerParams} [params]
9601
9864
  * @return {StaggerFunction<Number>}
9602
9865
  */
9866
+
9603
9867
  /**
9604
9868
  * @overload
9605
9869
  * @param {String} val
9606
9870
  * @param {StaggerParams} [params]
9607
9871
  * @return {StaggerFunction<String>}
9608
9872
  */
9873
+
9609
9874
  /**
9610
9875
  * @overload
9611
9876
  * @param {[Number, Number]} val
9612
9877
  * @param {StaggerParams} [params]
9613
9878
  * @return {StaggerFunction<Number>}
9614
9879
  */
9880
+
9615
9881
  /**
9616
9882
  * @overload
9617
9883
  * @param {[String, String]} val
9618
9884
  * @param {StaggerParams} [params]
9619
9885
  * @return {StaggerFunction<String>}
9620
9886
  */
9887
+
9621
9888
  /**
9622
9889
  * @param {Number|String|[Number, Number]|[String, String]} val The staggered value or range
9623
9890
  * @param {StaggerParams} [params] The stagger parameters
@@ -9626,6 +9893,7 @@
9626
9893
  const stagger = (val, params = {}) => {
9627
9894
  let values = [];
9628
9895
  let maxValue = 0;
9896
+ let cachedOffset;
9629
9897
  const from = params.from;
9630
9898
  const reversed = params.reversed;
9631
9899
  const ease = params.ease;
@@ -9633,12 +9901,14 @@
9633
9901
  const hasSpring = hasEasing && !isUnd(/** @type {Spring} */(ease).ease);
9634
9902
  const staggerEase = hasSpring ? /** @type {Spring} */(ease).ease : hasEasing ? parseEase(ease) : null;
9635
9903
  const grid = params.grid;
9904
+ const autoGrid = grid === true;
9636
9905
  const axis = params.axis;
9637
9906
  const customTotal = params.total;
9638
9907
  const fromFirst = isUnd(from) || from === 0 || from === 'first';
9639
9908
  const fromCenter = from === 'center';
9640
9909
  const fromLast = from === 'last';
9641
9910
  const fromRandom = from === 'random';
9911
+ const fromArr = isArr(from);
9642
9912
  const isRange = isArr(val);
9643
9913
  const useProp = params.use;
9644
9914
  const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
@@ -9646,40 +9916,129 @@
9646
9916
  const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
9647
9917
  const start = params.start || 0 + (isRange ? val1 : 0);
9648
9918
  let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
9649
- return (target, i, t, tl) => {
9919
+ return (target, i, t, _, tl) => {
9650
9920
  const [ registeredTarget ] = registerTargets(target);
9651
- const total = isUnd(customTotal) ? t : customTotal;
9921
+ const total = isUnd(customTotal) ? t.length : customTotal;
9652
9922
  const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
9653
9923
  const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
9654
9924
  if (fromCenter) fromIndex = (total - 1) / 2;
9655
9925
  if (fromLast) fromIndex = total - 1;
9656
9926
  if (!values.length) {
9657
- for (let index = 0; index < total; index++) {
9658
- if (!grid) {
9659
- values.push(abs(fromIndex - index));
9927
+ if (autoGrid) {
9928
+ let hasPositions = true;
9929
+ let minPosX = Infinity;
9930
+ let minPosY = Infinity;
9931
+ let maxPosX = -Infinity;
9932
+ let maxPosY = -Infinity;
9933
+ const pxArr = [];
9934
+ const pyArr = [];
9935
+ for (let index = 0; index < total; index++) {
9936
+ const el = t[index];
9937
+ let px = 0;
9938
+ let py = 0;
9939
+ let found = false;
9940
+ if (el && isFnc(el.getBoundingClientRect)) {
9941
+ const rect = el.getBoundingClientRect();
9942
+ px = rect.left + rect.width / 2;
9943
+ py = rect.top + rect.height / 2;
9944
+ found = true;
9945
+ } else {
9946
+ const obj = /** @type {JSTarget} */(el);
9947
+ if (obj && isNum(obj.x) && isNum(obj.y)) {
9948
+ px = obj.x;
9949
+ py = obj.y;
9950
+ found = true;
9951
+ }
9952
+ }
9953
+ if (!found) {
9954
+ hasPositions = false;
9955
+ break;
9956
+ }
9957
+ pxArr.push(px);
9958
+ pyArr.push(py);
9959
+ if (px < minPosX) minPosX = px;
9960
+ if (py < minPosY) minPosY = py;
9961
+ if (px > maxPosX) maxPosX = px;
9962
+ if (py > maxPosY) maxPosY = py;
9963
+ }
9964
+ if (hasPositions) {
9965
+ let fX = pxArr[0];
9966
+ let fY = pyArr[0];
9967
+ if (fromArr) {
9968
+ fX = minPosX + from[0] * (maxPosX - minPosX);
9969
+ fY = minPosY + from[1] * (maxPosY - minPosY);
9970
+ } else if (fromCenter) {
9971
+ fX = (minPosX + maxPosX) / 2;
9972
+ fY = (minPosY + maxPosY) / 2;
9973
+ } else if (fromLast) {
9974
+ fX = pxArr[total - 1];
9975
+ fY = pyArr[total - 1];
9976
+ } else if (isNum(from)) {
9977
+ fX = pxArr[from];
9978
+ fY = pyArr[from];
9979
+ }
9980
+ for (let index = 0; index < total; index++) {
9981
+ const distanceX = fX - pxArr[index];
9982
+ const distanceY = fY - pyArr[index];
9983
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9984
+ if (axis === 'x') value = -distanceX;
9985
+ if (axis === 'y') value = -distanceY;
9986
+ values.push(value);
9987
+ }
9988
+ let minDist = Infinity;
9989
+ for (let index = 0, l = values.length; index < l; index++) {
9990
+ const absVal = abs(values[index]);
9991
+ if (absVal > 0 && absVal < minDist) minDist = absVal;
9992
+ }
9993
+ if (minDist > 0 && minDist < Infinity) {
9994
+ for (let index = 0, l = values.length; index < l; index++) {
9995
+ values[index] = values[index] / minDist;
9996
+ }
9997
+ }
9660
9998
  } else {
9661
- const fromX = !fromCenter ? fromIndex % grid[0] : (grid[0] - 1) / 2;
9662
- const fromY = !fromCenter ? floor(fromIndex / grid[0]) : (grid[1] - 1) / 2;
9663
- const toX = index % grid[0];
9664
- const toY = floor(index / grid[0]);
9665
- const distanceX = fromX - toX;
9666
- const distanceY = fromY - toY;
9667
- let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9668
- if (axis === 'x') value = -distanceX;
9669
- if (axis === 'y') value = -distanceY;
9670
- values.push(value);
9999
+ for (let index = 0; index < total; index++) {
10000
+ values.push(abs(fromIndex - index));
10001
+ }
10002
+ }
10003
+ } else {
10004
+ for (let index = 0; index < total; index++) {
10005
+ if (!grid) {
10006
+ values.push(abs(fromIndex - index));
10007
+ } else {
10008
+ let fromX, fromY;
10009
+ if (fromArr) {
10010
+ fromX = from[0] * (grid[0] - 1);
10011
+ fromY = from[1] * (grid[1] - 1);
10012
+ } else if (fromCenter) {
10013
+ fromX = (grid[0] - 1) / 2;
10014
+ fromY = (grid[1] - 1) / 2;
10015
+ } else {
10016
+ fromX = fromIndex % grid[0];
10017
+ fromY = floor(fromIndex / grid[0]);
10018
+ }
10019
+ const toX = index % grid[0];
10020
+ const toY = floor(index / grid[0]);
10021
+ const distanceX = fromX - toX;
10022
+ const distanceY = fromY - toY;
10023
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY);
10024
+ if (axis === 'x') value = -distanceX;
10025
+ if (axis === 'y') value = -distanceY;
10026
+ values.push(value);
10027
+ }
9671
10028
  }
9672
- maxValue = max(...values);
9673
10029
  }
10030
+ maxValue = max(...values);
9674
10031
  if (staggerEase) values = values.map(val => staggerEase(val / maxValue) * maxValue);
9675
10032
  if (reversed) values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
9676
10033
  if (fromRandom) values = shuffle(values);
9677
10034
  }
9678
10035
  const spacing = isRange ? (val2 - val1) / maxValue : val1;
9679
- const offset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
10036
+ if (isUnd(cachedOffset)) {
10037
+ cachedOffset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
10038
+ }
9680
10039
  /** @type {String|Number} */
9681
- let output = offset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
9682
- if (params.modifier) output = params.modifier(output);
10040
+ let output = cachedOffset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
10041
+ if (params.modifier) output = params.modifier(/** @type {Number} */(output));
9683
10042
  if (unitMatch) output = `${output}${unitMatch[2]}`;
9684
10043
  return output;
9685
10044
  }
@@ -9688,11 +10047,13 @@
9688
10047
  var index$2 = /*#__PURE__*/Object.freeze({
9689
10048
  __proto__: null,
9690
10049
  $: registerTargets,
10050
+ addChild: addChild,
9691
10051
  clamp: clamp,
9692
10052
  cleanInlineStyles: cleanInlineStyles,
9693
10053
  createSeededRandom: createSeededRandom,
9694
10054
  damp: damp,
9695
10055
  degToRad: degToRad,
10056
+ forEachChildren: forEachChildren,
9696
10057
  get: get,
9697
10058
  keepTime: keepTime,
9698
10059
  lerp: lerp,
@@ -9703,6 +10064,7 @@
9703
10064
  random: random,
9704
10065
  randomPick: randomPick,
9705
10066
  remove: remove,
10067
+ removeChild: removeChild,
9706
10068
  round: round,
9707
10069
  roundPad: roundPad,
9708
10070
  set: set,
@@ -9902,7 +10264,7 @@
9902
10264
  * @param {Number} [precision]
9903
10265
  * @return {FunctionValue}
9904
10266
  */
9905
- const morphTo = (path2, precision = .33) => ($path1) => {
10267
+ const morphTo = (path2, precision = .33) => ($path1, index, total, prevTween) => {
9906
10268
  const tagName1 = ($path1.tagName || '').toLowerCase();
9907
10269
  if (!tagName1.match(/^(path|polygon|polyline)$/)) {
9908
10270
  throw new Error(`Can't morph a <${$path1.tagName}> SVG element. Use <path>, <polygon> or <polyline>.`);
@@ -9917,7 +10279,7 @@
9917
10279
  }
9918
10280
  const isPath = $path1.tagName === 'path';
9919
10281
  const separator = isPath ? ' ' : ',';
9920
- const previousPoints = $path1[morphPointsSymbol];
10282
+ const previousPoints = prevTween ? prevTween._value : null;
9921
10283
  if (previousPoints) $path1.setAttribute(isPath ? 'd' : 'points', previousPoints);
9922
10284
 
9923
10285
  let v1 = '', v2 = '';
@@ -9939,8 +10301,6 @@
9939
10301
  }
9940
10302
  }
9941
10303
 
9942
- $path1[morphPointsSymbol] = v2;
9943
-
9944
10304
  return [v1, v2];
9945
10305
  };
9946
10306
 
@@ -10126,7 +10486,7 @@
10126
10486
  */
10127
10487
  class TextSplitter {
10128
10488
  /**
10129
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10489
+ * @param {Element|NodeList|String|Array<Element>} target
10130
10490
  * @param {TextSplitterParams} [parameters]
10131
10491
  */
10132
10492
  constructor(target, parameters = {}) {
@@ -10198,11 +10558,11 @@
10198
10558
  }
10199
10559
 
10200
10560
  /**
10201
- * @param {(...args: any[]) => Tickable | (() => void)} effect
10561
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
10202
10562
  * @return this
10203
10563
  */
10204
10564
  addEffect(effect) {
10205
- if (!isFnc(effect)) return console.warn('Effect must return a function.');
10565
+ if (!isFnc(effect)) { console.warn('Effect must return a function.'); return this; }
10206
10566
  const refreshableEffect = keepTime(effect);
10207
10567
  this.effects.push(refreshableEffect);
10208
10568
  if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
@@ -10408,7 +10768,7 @@
10408
10768
  }
10409
10769
 
10410
10770
  /**
10411
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10771
+ * @param {Element|NodeList|String|Array<Element>} target
10412
10772
  * @param {TextSplitterParams} [parameters]
10413
10773
  * @return {TextSplitter}
10414
10774
  */
@@ -10426,9 +10786,261 @@
10426
10786
  return new TextSplitter(target, parameters);
10427
10787
  };
10428
10788
 
10789
+
10790
+
10791
+ /**
10792
+ * '-' is the range operator; place it at the start or end of the string to use it as a literal (e.g. '-abc' or 'abc-')
10793
+ * @param {String} str
10794
+ * @return {String}
10795
+ */
10796
+ const expandCharRanges = (str) => {
10797
+ let result = '';
10798
+ for (let i = 0, l = str.length; i < l; i++) {
10799
+ if (i + 2 < l && str[i + 1] === '-' && str.charCodeAt(i) < str.charCodeAt(i + 2)) {
10800
+ const start = str.charCodeAt(i);
10801
+ const end = str.charCodeAt(i + 2);
10802
+ for (let c = start; c <= end; c++) result += String.fromCharCode(c);
10803
+ i += 2;
10804
+ } else {
10805
+ result += str[i];
10806
+ }
10807
+ }
10808
+ return result;
10809
+ };
10810
+
10811
+ const charSets = {
10812
+ lowercase: 'a-z',
10813
+ uppercase: 'A-Z',
10814
+ numbers: '0-9',
10815
+ symbols: '!%#_|*+=',
10816
+ braille: '⠀-⣿',
10817
+ blocks: '▀-▟',
10818
+ shades: '░-▓',
10819
+ };
10820
+
10821
+ const originalTexts = new WeakMap();
10822
+
10823
+ /**
10824
+ * Returns a function-based tween value that scrambles the target's text content,
10825
+ * progressively revealing the original text.
10826
+ *
10827
+ * @param {ScrambleTextParams} [params]
10828
+ * @return {FunctionValue}
10829
+ */
10830
+ const scrambleText = (params = {}) => {
10831
+ if (!params) params = {};
10832
+ const charsParam = params.chars;
10833
+ const easeFn = parseEase(params.ease || 'linear');
10834
+ const text = params.text;
10835
+ const fromParam = params.from;
10836
+ const reversed = params.reversed || false;
10837
+ const perturbation = params.perturbation || 0;
10838
+ const cursorParam = params.cursor;
10839
+ const cursorChars = cursorParam === true ? '_'
10840
+ : typeof cursorParam === 'number' ? String.fromCharCode(cursorParam)
10841
+ : typeof cursorParam === 'string' ? cursorParam
10842
+ : '';
10843
+ const cursorLen = cursorChars.length;
10844
+ const seed = params.seed || 0;
10845
+ const override = params.override !== undefined ? params.override : true;
10846
+ const revealRate = params.revealRate || 60;
10847
+ const interval = 1000 * globals.timeScale / revealRate;
10848
+ const settleDuration = params.settleDuration || 300 * globals.timeScale;
10849
+ const settleRate = params.settleRate || 30;
10850
+ const durationParam = params.duration;
10851
+ const revealDelayParam = params.revealDelay;
10852
+ const delayParam = params.delay;
10853
+ const onChange = params.onChange || noop;
10854
+
10855
+ return (target, index, targets, prevTween) => {
10856
+ const rawChars = typeof charsParam === 'function' ? charsParam(target, index, targets) : (charsParam || 'a-zA-Z0-9!%#_');
10857
+ const characters = expandCharRanges(charSets[rawChars] || rawChars);
10858
+ const totalChars = characters.length - 1;
10859
+ const duration = typeof durationParam === 'function' ? durationParam(target, index, targets) : durationParam;
10860
+ const revealDelay = typeof revealDelayParam === 'function' ? revealDelayParam(target, index, targets) : (revealDelayParam || 0);
10861
+ const delay = typeof delayParam === 'function' ? delayParam(target, index, targets) : (delayParam || 0);
10862
+ const rng = seed ? createSeededRandom(seed) : createSeededRandom();
10863
+ if (!originalTexts.has(target)) originalTexts.set(target, target.textContent);
10864
+ const startingText = prevTween ? prevTween._value : target.textContent;
10865
+ const targetText = text !== undefined
10866
+ ? (typeof text === 'function' ? text(target, index, targets) : text)
10867
+ : prevTween ? prevTween._value
10868
+ : originalTexts.get(target);
10869
+ const settledText = targetText === ' ' || targetText === '&nbsp;' ? ' ' : targetText;
10870
+ const startLength = startingText === ' ' ? 0 : startingText.length;
10871
+ const endLength = settledText.length;
10872
+ const overrideChars = override === true ? characters
10873
+ : typeof override === 'string' && override.length > 0 ? expandCharRanges(charSets[/** @type {String} */(override)] || /** @type {String} */(override))
10874
+ : null;
10875
+ const totalOverrideChars = overrideChars ? overrideChars.length - 1 : 0;
10876
+ // Space override uses &nbsp; so the browser doesn't collapse consecutive spaces in innerHTML
10877
+ const overrideChar = override === ' ' ? ' ' : null;
10878
+ // When starting from blank, only animate the target text length to avoid padding beyond it
10879
+ const animLength = override === '' ? endLength : Math.max(startLength, endLength);
10880
+ // Compute total duration from interval spacing and settle time, or use the explicit duration
10881
+ const animDuration = duration > 0 ? duration : (animLength - 1) * interval + settleDuration;
10882
+ const computedDuration = round$1((animDuration + revealDelay) / globals.timeScale, 0) * globals.timeScale;
10883
+ const revealDelayRatio = revealDelay > 0 ? round$1(revealDelay / computedDuration, 12) : 0;
10884
+ // Auto-resolve reveal direction: shrinking text reveals from right, growing from left
10885
+ const resolvedFrom = fromParam === undefined || fromParam === 'auto' ? (endLength < startLength ? 'right' : 'left') : fromParam;
10886
+ const charOrder = new Int32Array(animLength);
10887
+ if (resolvedFrom === 'random') {
10888
+ for (let i = 0; i < animLength; i++) charOrder[i] = i;
10889
+ for (let i = animLength - 1; i > 0; i--) {
10890
+ const j = rng(0, i);
10891
+ const t = charOrder[i]; charOrder[i] = charOrder[j]; charOrder[j] = t;
10892
+ }
10893
+ } else {
10894
+ const ref = resolvedFrom === 'right' ? (override === '' || !startLength ? animLength : startLength) - 1
10895
+ : resolvedFrom === 'center' ? ((override === '' || !startLength ? animLength : startLength) - 1) / 2
10896
+ : typeof resolvedFrom === 'number' ? resolvedFrom
10897
+ : 0;
10898
+ const abs = Math.abs;
10899
+ const indices = new Array(animLength);
10900
+ for (let i = 0; i < animLength; i++) indices[i] = i;
10901
+ indices.sort((a, b) => abs(a - ref) - abs(b - ref));
10902
+ for (let i = 0; i < animLength; i++) charOrder[indices[i]] = i;
10903
+ }
10904
+ if (reversed) {
10905
+ const last = animLength - 1;
10906
+ for (let i = 0; i < animLength; i++) charOrder[i] = last - charOrder[i];
10907
+ }
10908
+ // settleRatio is the fraction of the animation each character spends in the active scrambling zone
10909
+ const settleRatio = round$1(settleDuration / animDuration, 12);
10910
+ // settleSpacing is the time gap between consecutive characters entering the active zone
10911
+ const settleSpacing = round$1((1 - settleRatio) / animLength, 12);
10912
+ const cursorZone = cursorLen * settleSpacing;
10913
+ // stepRatio controls how often scramble characters refresh (based on settleRate)
10914
+ const stepRatio = round$1(1000 * globals.timeScale / (settleRate * computedDuration), 12);
10915
+ // Pre-compute per-character start and settle times
10916
+ const charStarts = new Float32Array(animLength);
10917
+ const charEnds = new Float32Array(animLength);
10918
+ const scale = perturbation > 0 ? perturbation * settleRatio : 0;
10919
+ for (let c = 0; c < animLength; c++) {
10920
+ const so = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
10921
+ const eo = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
10922
+ charStarts[c] = charOrder[c] * settleSpacing + so;
10923
+ charEnds[c] = Math.ceil((charStarts[c] + settleRatio + eo) / stepRatio) * stepRatio;
10924
+ }
10925
+ // When text shrinks with non-sequential from modes, delay target settle times past all extras
10926
+ if (endLength < animLength && resolvedFrom !== 'left' && resolvedFrom !== 'right' && resolvedFrom !== 'random') {
10927
+ let maxExtraEnd = 0;
10928
+ for (let c = endLength; c < animLength; c++) {
10929
+ if (charEnds[c] > maxExtraEnd) maxExtraEnd = charEnds[c];
10930
+ }
10931
+ const targets = new Array(endLength);
10932
+ for (let c = 0; c < endLength; c++) targets[c] = c;
10933
+ targets.sort((a, b) => charOrder[a] - charOrder[b]);
10934
+ const targetSpacing = (1 - maxExtraEnd) / endLength;
10935
+ for (let i = 0; i < endLength; i++) {
10936
+ const revealTime = maxExtraEnd + i * targetSpacing;
10937
+ if (revealTime > charEnds[targets[i]]) {
10938
+ charEnds[targets[i]] = revealTime;
10939
+ }
10940
+ }
10941
+ }
10942
+ // charCache holds the current scramble character for each position, refreshed at settleRate
10943
+ const charCache = new Array(animLength);
10944
+ for (let c = 0; c < animLength; c++) {
10945
+ charCache[c] = characters[rng(0, totalChars)];
10946
+ }
10947
+ // overrideCache holds scramble characters for the starting text (override: true or custom string)
10948
+ const overrideCache = overrideChars ? (overrideChars === characters ? charCache : new Array(animLength)) : null;
10949
+ if (overrideCache && overrideCache !== charCache) {
10950
+ for (let c = 0; c < animLength; c++) {
10951
+ overrideCache[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, overrideChars.length - 1)];
10952
+ }
10953
+ }
10954
+ // Build the initial display text based on override mode
10955
+ let fillStartText = startingText;
10956
+ if (!prevTween) {
10957
+ if (override === '') {
10958
+ fillStartText = '';
10959
+ } else if (overrideChars) {
10960
+ fillStartText = '';
10961
+ for (let c = 0; c < startLength; c++) {
10962
+ fillStartText += startingText[c] === ' ' ? ' ' : /** @type {Array<String>} */(overrideCache)[c];
10963
+ }
10964
+ }
10965
+ }
10966
+
10967
+ let lastValue = -1;
10968
+ let lastStep = -1;
10969
+ let scrambled = '';
10970
+ const hasOverride = override !== '';
10971
+ const hasOverrideChars = !!overrideChars;
10972
+ const hasCursor = cursorLen > 0;
10973
+
10974
+ return {
10975
+ from: 0,
10976
+ to: 1,
10977
+ duration: computedDuration,
10978
+ delay: delay,
10979
+ ease: 'linear',
10980
+ modifier: (v) => {
10981
+ if (v === lastValue) return scrambled;
10982
+ lastValue = v;
10983
+ if (delay > 0 && v <= 0) { scrambled = startingText; return startingText; }
10984
+ if (v <= 0) { scrambled = fillStartText; return fillStartText; }
10985
+ if (v >= 1) { scrambled = settledText; return settledText; }
10986
+ scrambled = '';
10987
+ // Only refresh scramble characters when we cross a settleRate step boundary
10988
+ const currentStep = (v / stepRatio) | 0;
10989
+ const refreshChars = currentStep !== lastStep;
10990
+ if (refreshChars) lastStep = currentStep;
10991
+ // Subtract delay ratio to get the effective animation progress
10992
+ const linear = revealDelayRatio > 0 ? (v - revealDelayRatio) / (1 - revealDelayRatio) : v;
10993
+ const t = linear > 0 ? easeFn(linear) : 0;
10994
+ for (let c = 0; c < animLength; c++) {
10995
+ // Each character has its own start/end window based on its reveal order
10996
+ const charStart = charStarts[c];
10997
+ const charEnd = charEnds[c];
10998
+ // Settled zone: character has finished its transition
10999
+ if (t >= charEnd) {
11000
+ if (c < endLength) scrambled += settledText[c];
11001
+ continue;
11002
+ }
11003
+ // Pre-transition zone: reveal wave hasn't reached this character yet
11004
+ if (t <= 0 || t < charStart) {
11005
+ if (hasOverride && c < startLength) {
11006
+ if (hasOverrideChars) {
11007
+ if (startingText[c] === ' ') {
11008
+ scrambled += ' ';
11009
+ } else {
11010
+ if (refreshChars) /** @type {Array<String>} */(overrideCache)[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, totalOverrideChars)];
11011
+ scrambled += /** @type {Array<String>} */(overrideCache)[c];
11012
+ }
11013
+ } else {
11014
+ // Default (override: false): show the original starting text
11015
+ scrambled += startingText[c];
11016
+ }
11017
+ }
11018
+ continue;
11019
+ }
11020
+ // Active zone: character is between charStart and charEnd
11021
+ const isSpace = (c < endLength && settledText[c] === ' ') || (c < startLength && startingText[c] === ' ');
11022
+ if (isSpace) {
11023
+ scrambled += ' ';
11024
+ } else if (hasCursor && t - charStart < cursorZone) {
11025
+ // Cursor sub-zone: show cursor character based on position within cursor width
11026
+ scrambled += cursorChars[cursorLen - 1 - (((t - charStart) / settleSpacing) | 0)];
11027
+ } else {
11028
+ // Scramble zone: show cycling random characters
11029
+ if (refreshChars) charCache[c] = characters[rng(0, totalChars)];
11030
+ scrambled += charCache[c];
11031
+ }
11032
+ }
11033
+ if (refreshChars) onChange(scrambled, t);
11034
+ return scrambled;
11035
+ }
11036
+ }
11037
+ }
11038
+ };
11039
+
10429
11040
  var index = /*#__PURE__*/Object.freeze({
10430
11041
  __proto__: null,
10431
11042
  TextSplitter: TextSplitter,
11043
+ scrambleText: scrambleText,
10432
11044
  split: split,
10433
11045
  splitText: splitText
10434
11046
  });
@@ -10445,6 +11057,7 @@
10445
11057
  exports.Timeline = Timeline;
10446
11058
  exports.Timer = Timer;
10447
11059
  exports.WAAPIAnimation = WAAPIAnimation;
11060
+ exports.addChild = addChild;
10448
11061
  exports.animate = animate;
10449
11062
  exports.clamp = clamp;
10450
11063
  exports.cleanInlineStyles = cleanInlineStyles;
@@ -10464,7 +11077,9 @@
10464
11077
  exports.eases = eases;
10465
11078
  exports.easings = index$3;
10466
11079
  exports.engine = engine;
11080
+ exports.forEachChildren = forEachChildren;
10467
11081
  exports.get = get;
11082
+ exports.globals = globals;
10468
11083
  exports.irregular = irregular;
10469
11084
  exports.keepTime = keepTime;
10470
11085
  exports.lerp = lerp;
@@ -10478,8 +11093,10 @@
10478
11093
  exports.random = random;
10479
11094
  exports.randomPick = randomPick;
10480
11095
  exports.remove = remove;
11096
+ exports.removeChild = removeChild;
10481
11097
  exports.round = round;
10482
11098
  exports.roundPad = roundPad;
11099
+ exports.scrambleText = scrambleText;
10483
11100
  exports.scrollContainers = scrollContainers;
10484
11101
  exports.set = set;
10485
11102
  exports.shuffle = shuffle;