animejs 4.3.5 → 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 +9 -12
  2. package/dist/bundles/anime.esm.js +1048 -421
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +1053 -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 +47 -24
  82. package/dist/modules/layout/layout.d.ts +4 -3
  83. package/dist/modules/layout/layout.js +48 -25
  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.5
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.5', 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;
1088
1129
  }
1130
+ pos++;
1089
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
+ }
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);
1161
+ }
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;
1090
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
- );
4077
- }
4078
- removeTargetsFromRenderable(
4079
- targetsArray,
4080
- /** @type {Renderable} */(renderable),
4081
- propertyName
4082
- );
4083
- return targetsArray;
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();
4157
+ }
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;
@@ -7828,6 +8084,8 @@
7828
8084
 
7829
8085
 
7830
8086
 
8087
+
8088
+
7831
8089
  /**
7832
8090
  * @typedef {DOMTargetSelector|Array<DOMTargetSelector>} LayoutChildrenParam
7833
8091
  */
@@ -7849,6 +8107,7 @@
7849
8107
 
7850
8108
  /**
7851
8109
  * @typedef {Object} LayoutSpecificAnimationParams
8110
+ * @property {Number|String} [id]
7852
8111
  * @property {Number|FunctionValue} [delay]
7853
8112
  * @property {Number|FunctionValue} [duration]
7854
8113
  * @property {EasingParam|FunctionValue} [ease]
@@ -7891,7 +8150,7 @@
7891
8150
  * @property {String} id
7892
8151
  * @property {DOMTarget} $el
7893
8152
  * @property {Number} index
7894
- * @property {Number} total
8153
+ * @property {Array<DOMTarget>} targets
7895
8154
  * @property {Number} delay
7896
8155
  * @property {Number} duration
7897
8156
  * @property {EasingParam} ease
@@ -8035,7 +8294,7 @@
8035
8294
  node.$measure = $el;
8036
8295
  node.id = dataId;
8037
8296
  node.index = 0;
8038
- node.total = 1;
8297
+ node.targets = null;
8039
8298
  node.delay = 0;
8040
8299
  node.duration = 0;
8041
8300
  node.ease = null;
@@ -8221,12 +8480,12 @@
8221
8480
  * @param {LayoutAnimationTimingsParams} params
8222
8481
  */
8223
8482
  const updateNodeTimingParams = (node, params) => {
8224
- 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);
8225
8484
  const keyEasing = isFnc(easeFunctionResult) ? easeFunctionResult : params.ease;
8226
8485
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
8227
8486
  node.ease = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
8228
- node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.total);
8229
- 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);
8230
8489
  };
8231
8490
 
8232
8491
  /**
@@ -8599,10 +8858,12 @@
8599
8858
 
8600
8859
  const inRootNodeIds = new Set();
8601
8860
  // Update index and total for inital timing calculation
8602
- let index = 0, total = this.nodes.size;
8861
+ let index = 0;
8862
+ const allNodeTargets = [];
8863
+ this.nodes.forEach((node) => { allNodeTargets.push(node.$el); });
8603
8864
  this.nodes.forEach((node, id) => {
8604
8865
  node.index = index++;
8605
- node.total = total;
8866
+ node.targets = allNodeTargets;
8606
8867
  // Track ids of nodes that belong to the current root to filter detached matches
8607
8868
  if (node && node.measuredIsInsideRoot) {
8608
8869
  inRootNodeIds.add(id);
@@ -8714,8 +8975,8 @@
8714
8975
  this.params = params;
8715
8976
  /** @type {DOMTarget} */
8716
8977
  this.root = /** @type {DOMTarget} */(registerTargets(root)[0]);
8717
- /** @type {Number} */
8718
- this.id = layoutId++;
8978
+ /** @type {Number|String} */
8979
+ this.id = params.id || layoutId++;
8719
8980
  /** @type {LayoutChildrenParam} */
8720
8981
  this.children = params.children || '*';
8721
8982
  /** @type {Boolean} */
@@ -8838,7 +9099,9 @@
8838
9099
  duration: setValue(params.duration, this.params.duration),
8839
9100
  };
8840
9101
  /** @type {TimelineParams} */
8841
- const tlParams = {};
9102
+ const tlParams = {
9103
+ id: this.id
9104
+ };
8842
9105
  const onComplete = setValue(params.onComplete, this.params.onComplete);
8843
9106
  const onPause = setValue(params.onPause, this.params.onPause);
8844
9107
  for (let name in defaults) {
@@ -8851,6 +9114,13 @@
8851
9114
  }
8852
9115
  }
8853
9116
  tlParams.onComplete = () => {
9117
+ const ap = /** @type {ScrollObserver} */(params.autoplay);
9118
+ const ed = globals.editor;
9119
+ const isScrollControled = (ap && ap.linked) || (ed && ed.showPanel);
9120
+ if (isScrollControled) {
9121
+ if (onComplete) onComplete(this.timeline);
9122
+ return;
9123
+ }
8854
9124
  // Make sure to call .cancel() after restoreNodeInlineStyles(node); otehrwise the commited styles get reverted
8855
9125
  if (this.transformAnimation) this.transformAnimation.cancel();
8856
9126
  newState.forEachRootNode(node => {
@@ -8872,6 +9142,13 @@
8872
9142
  });
8873
9143
  };
8874
9144
  tlParams.onPause = () => {
9145
+ const ap = /** @type {ScrollObserver} */(params.autoplay);
9146
+ const isScrollControled = ap && ap.linked;
9147
+ if (isScrollControled) {
9148
+ if (onComplete) onComplete(this.timeline);
9149
+ if (onPause) onPause(this.timeline);
9150
+ return;
9151
+ }
8875
9152
  if (!this.root.classList.contains('is-animated')) return;
8876
9153
  if (this.transformAnimation) this.transformAnimation.cancel();
8877
9154
  newState.forEachRootNode(restoreNodeVisualState);
@@ -9038,41 +9315,39 @@
9038
9315
  animatedParent = animatedParent.parentNode;
9039
9316
  }
9040
9317
 
9041
- const animatingTotal = animating.length;
9042
-
9043
9318
  // Root is always animated first in sync with the first child (animating.length is the total of children)
9044
9319
  if (node === rootNode) {
9045
9320
  node.index = 0;
9046
- node.total = animatingTotal;
9321
+ node.targets = animating;
9047
9322
  updateNodeTimingParams(node, animationTimings);
9048
9323
  } else if (node.isEntering) {
9049
9324
  node.index = animatedParent ? animatedParent.index : enteringIndex;
9050
- node.total = animatedParent ? animatingTotal : entering.length;
9325
+ node.targets = animatedParent ? animating : entering;
9051
9326
  updateNodeTimingParams(node, enterFromTimings);
9052
9327
  enteringIndex++;
9053
9328
  } else if (node.isLeaving) {
9054
9329
  node.index = animatedParent ? animatedParent.index : leavingIndex;
9055
- node.total = animatedParent ? animatingTotal : leaving.length;
9330
+ node.targets = animatedParent ? animating : leaving;
9056
9331
  leavingIndex++;
9057
9332
  updateNodeTimingParams(node, leaveToTimings);
9058
9333
  } else if (node.isTarget) {
9059
9334
  node.index = animatingIndex++;
9060
- node.total = animatingTotal;
9335
+ node.targets = animating;
9061
9336
  updateNodeTimingParams(node, animationTimings);
9062
9337
  } else {
9063
9338
  node.index = animatedParent ? animatedParent.index : 0;
9064
- node.total = animatingTotal;
9339
+ node.targets = animating;
9065
9340
  updateNodeTimingParams(node, swapAtTimings);
9066
9341
  }
9067
9342
 
9068
9343
  // Make sure the old state node has its inex and total values up to date for valid "from" function values calculation
9069
9344
  oldStateNode.index = node.index;
9070
- oldStateNode.total = node.total;
9345
+ oldStateNode.targets = node.targets;
9071
9346
 
9072
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
9073
9348
  for (let prop in nodeProperties) {
9074
- nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.total);
9075
- 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);
9076
9351
  }
9077
9352
 
9078
9353
  // Use a 1px tolerance to detect dimensions changes to prevent width / height animations on barelly visible elements
@@ -9298,8 +9573,8 @@
9298
9573
  }
9299
9574
  $el.style.transform = oldState.getComputedValue($el, 'transform');
9300
9575
  if (animatedSwap.includes($el)) {
9301
- node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.total);
9302
- 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);
9303
9578
  }
9304
9579
  }
9305
9580
  this.transformAnimation = waapi.animate(transformed, {
@@ -9314,7 +9589,7 @@
9314
9589
  if (!animatedSwap.includes($el)) return newValue;
9315
9590
  const oldValue = oldState.getComputedValue($el, 'transform');
9316
9591
  const node = newState.getNode($el);
9317
- 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]
9318
9593
  },
9319
9594
  autoplay: false,
9320
9595
  // persist: true,
@@ -9371,10 +9646,13 @@
9371
9646
  const result = fn(...args);
9372
9647
  return new Proxy(noop, {
9373
9648
  apply: (_, __, [v]) => result(v),
9374
- get: (_, prop) => chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9375
- const nextResult = chainables[prop](...nextArgs);
9376
- return (/**@type {Number|String} */v) => nextResult(result(v));
9377
- })
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
+ }
9378
9656
  });
9379
9657
  }
9380
9658
  };
@@ -9585,24 +9863,28 @@
9585
9863
  * @param {StaggerParams} [params]
9586
9864
  * @return {StaggerFunction<Number>}
9587
9865
  */
9866
+
9588
9867
  /**
9589
9868
  * @overload
9590
9869
  * @param {String} val
9591
9870
  * @param {StaggerParams} [params]
9592
9871
  * @return {StaggerFunction<String>}
9593
9872
  */
9873
+
9594
9874
  /**
9595
9875
  * @overload
9596
9876
  * @param {[Number, Number]} val
9597
9877
  * @param {StaggerParams} [params]
9598
9878
  * @return {StaggerFunction<Number>}
9599
9879
  */
9880
+
9600
9881
  /**
9601
9882
  * @overload
9602
9883
  * @param {[String, String]} val
9603
9884
  * @param {StaggerParams} [params]
9604
9885
  * @return {StaggerFunction<String>}
9605
9886
  */
9887
+
9606
9888
  /**
9607
9889
  * @param {Number|String|[Number, Number]|[String, String]} val The staggered value or range
9608
9890
  * @param {StaggerParams} [params] The stagger parameters
@@ -9611,6 +9893,7 @@
9611
9893
  const stagger = (val, params = {}) => {
9612
9894
  let values = [];
9613
9895
  let maxValue = 0;
9896
+ let cachedOffset;
9614
9897
  const from = params.from;
9615
9898
  const reversed = params.reversed;
9616
9899
  const ease = params.ease;
@@ -9618,12 +9901,14 @@
9618
9901
  const hasSpring = hasEasing && !isUnd(/** @type {Spring} */(ease).ease);
9619
9902
  const staggerEase = hasSpring ? /** @type {Spring} */(ease).ease : hasEasing ? parseEase(ease) : null;
9620
9903
  const grid = params.grid;
9904
+ const autoGrid = grid === true;
9621
9905
  const axis = params.axis;
9622
9906
  const customTotal = params.total;
9623
9907
  const fromFirst = isUnd(from) || from === 0 || from === 'first';
9624
9908
  const fromCenter = from === 'center';
9625
9909
  const fromLast = from === 'last';
9626
9910
  const fromRandom = from === 'random';
9911
+ const fromArr = isArr(from);
9627
9912
  const isRange = isArr(val);
9628
9913
  const useProp = params.use;
9629
9914
  const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
@@ -9631,40 +9916,129 @@
9631
9916
  const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
9632
9917
  const start = params.start || 0 + (isRange ? val1 : 0);
9633
9918
  let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
9634
- return (target, i, t, tl) => {
9919
+ return (target, i, t, _, tl) => {
9635
9920
  const [ registeredTarget ] = registerTargets(target);
9636
- const total = isUnd(customTotal) ? t : customTotal;
9921
+ const total = isUnd(customTotal) ? t.length : customTotal;
9637
9922
  const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
9638
9923
  const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
9639
9924
  if (fromCenter) fromIndex = (total - 1) / 2;
9640
9925
  if (fromLast) fromIndex = total - 1;
9641
9926
  if (!values.length) {
9642
- for (let index = 0; index < total; index++) {
9643
- if (!grid) {
9644
- 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
+ }
9645
9998
  } else {
9646
- const fromX = !fromCenter ? fromIndex % grid[0] : (grid[0] - 1) / 2;
9647
- const fromY = !fromCenter ? floor(fromIndex / grid[0]) : (grid[1] - 1) / 2;
9648
- const toX = index % grid[0];
9649
- const toY = floor(index / grid[0]);
9650
- const distanceX = fromX - toX;
9651
- const distanceY = fromY - toY;
9652
- let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9653
- if (axis === 'x') value = -distanceX;
9654
- if (axis === 'y') value = -distanceY;
9655
- 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
+ }
9656
10028
  }
9657
- maxValue = max(...values);
9658
10029
  }
10030
+ maxValue = max(...values);
9659
10031
  if (staggerEase) values = values.map(val => staggerEase(val / maxValue) * maxValue);
9660
10032
  if (reversed) values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
9661
10033
  if (fromRandom) values = shuffle(values);
9662
10034
  }
9663
10035
  const spacing = isRange ? (val2 - val1) / maxValue : val1;
9664
- 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
+ }
9665
10039
  /** @type {String|Number} */
9666
- let output = offset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
9667
- 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));
9668
10042
  if (unitMatch) output = `${output}${unitMatch[2]}`;
9669
10043
  return output;
9670
10044
  }
@@ -9673,11 +10047,13 @@
9673
10047
  var index$2 = /*#__PURE__*/Object.freeze({
9674
10048
  __proto__: null,
9675
10049
  $: registerTargets,
10050
+ addChild: addChild,
9676
10051
  clamp: clamp,
9677
10052
  cleanInlineStyles: cleanInlineStyles,
9678
10053
  createSeededRandom: createSeededRandom,
9679
10054
  damp: damp,
9680
10055
  degToRad: degToRad,
10056
+ forEachChildren: forEachChildren,
9681
10057
  get: get,
9682
10058
  keepTime: keepTime,
9683
10059
  lerp: lerp,
@@ -9688,6 +10064,7 @@
9688
10064
  random: random,
9689
10065
  randomPick: randomPick,
9690
10066
  remove: remove,
10067
+ removeChild: removeChild,
9691
10068
  round: round,
9692
10069
  roundPad: roundPad,
9693
10070
  set: set,
@@ -9887,7 +10264,7 @@
9887
10264
  * @param {Number} [precision]
9888
10265
  * @return {FunctionValue}
9889
10266
  */
9890
- const morphTo = (path2, precision = .33) => ($path1) => {
10267
+ const morphTo = (path2, precision = .33) => ($path1, index, total, prevTween) => {
9891
10268
  const tagName1 = ($path1.tagName || '').toLowerCase();
9892
10269
  if (!tagName1.match(/^(path|polygon|polyline)$/)) {
9893
10270
  throw new Error(`Can't morph a <${$path1.tagName}> SVG element. Use <path>, <polygon> or <polyline>.`);
@@ -9902,7 +10279,7 @@
9902
10279
  }
9903
10280
  const isPath = $path1.tagName === 'path';
9904
10281
  const separator = isPath ? ' ' : ',';
9905
- const previousPoints = $path1[morphPointsSymbol];
10282
+ const previousPoints = prevTween ? prevTween._value : null;
9906
10283
  if (previousPoints) $path1.setAttribute(isPath ? 'd' : 'points', previousPoints);
9907
10284
 
9908
10285
  let v1 = '', v2 = '';
@@ -9924,8 +10301,6 @@
9924
10301
  }
9925
10302
  }
9926
10303
 
9927
- $path1[morphPointsSymbol] = v2;
9928
-
9929
10304
  return [v1, v2];
9930
10305
  };
9931
10306
 
@@ -10111,7 +10486,7 @@
10111
10486
  */
10112
10487
  class TextSplitter {
10113
10488
  /**
10114
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10489
+ * @param {Element|NodeList|String|Array<Element>} target
10115
10490
  * @param {TextSplitterParams} [parameters]
10116
10491
  */
10117
10492
  constructor(target, parameters = {}) {
@@ -10183,11 +10558,11 @@
10183
10558
  }
10184
10559
 
10185
10560
  /**
10186
- * @param {(...args: any[]) => Tickable | (() => void)} effect
10561
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
10187
10562
  * @return this
10188
10563
  */
10189
10564
  addEffect(effect) {
10190
- if (!isFnc(effect)) return console.warn('Effect must return a function.');
10565
+ if (!isFnc(effect)) { console.warn('Effect must return a function.'); return this; }
10191
10566
  const refreshableEffect = keepTime(effect);
10192
10567
  this.effects.push(refreshableEffect);
10193
10568
  if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
@@ -10393,7 +10768,7 @@
10393
10768
  }
10394
10769
 
10395
10770
  /**
10396
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10771
+ * @param {Element|NodeList|String|Array<Element>} target
10397
10772
  * @param {TextSplitterParams} [parameters]
10398
10773
  * @return {TextSplitter}
10399
10774
  */
@@ -10411,9 +10786,261 @@
10411
10786
  return new TextSplitter(target, parameters);
10412
10787
  };
10413
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
+
10414
11040
  var index = /*#__PURE__*/Object.freeze({
10415
11041
  __proto__: null,
10416
11042
  TextSplitter: TextSplitter,
11043
+ scrambleText: scrambleText,
10417
11044
  split: split,
10418
11045
  splitText: splitText
10419
11046
  });
@@ -10430,6 +11057,7 @@
10430
11057
  exports.Timeline = Timeline;
10431
11058
  exports.Timer = Timer;
10432
11059
  exports.WAAPIAnimation = WAAPIAnimation;
11060
+ exports.addChild = addChild;
10433
11061
  exports.animate = animate;
10434
11062
  exports.clamp = clamp;
10435
11063
  exports.cleanInlineStyles = cleanInlineStyles;
@@ -10449,7 +11077,9 @@
10449
11077
  exports.eases = eases;
10450
11078
  exports.easings = index$3;
10451
11079
  exports.engine = engine;
11080
+ exports.forEachChildren = forEachChildren;
10452
11081
  exports.get = get;
11082
+ exports.globals = globals;
10453
11083
  exports.irregular = irregular;
10454
11084
  exports.keepTime = keepTime;
10455
11085
  exports.lerp = lerp;
@@ -10463,8 +11093,10 @@
10463
11093
  exports.random = random;
10464
11094
  exports.randomPick = randomPick;
10465
11095
  exports.remove = remove;
11096
+ exports.removeChild = removeChild;
10466
11097
  exports.round = round;
10467
11098
  exports.roundPad = roundPad;
11099
+ exports.scrambleText = scrambleText;
10468
11100
  exports.scrollContainers = scrollContainers;
10469
11101
  exports.set = set;
10470
11102
  exports.shuffle = shuffle;