animejs 4.3.6 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +8 -11
  2. package/dist/bundles/anime.esm.js +1033 -421
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +1038 -421
  5. package/dist/bundles/anime.umd.min.js +2 -2
  6. package/dist/modules/animatable/animatable.cjs +1 -1
  7. package/dist/modules/animatable/animatable.js +2 -2
  8. package/dist/modules/animatable/index.cjs +1 -1
  9. package/dist/modules/animatable/index.js +1 -1
  10. package/dist/modules/animation/additive.cjs +1 -1
  11. package/dist/modules/animation/additive.js +1 -1
  12. package/dist/modules/animation/animation.cjs +38 -16
  13. package/dist/modules/animation/animation.d.ts +2 -2
  14. package/dist/modules/animation/animation.js +42 -20
  15. package/dist/modules/animation/composition.cjs +1 -1
  16. package/dist/modules/animation/composition.js +3 -3
  17. package/dist/modules/animation/index.cjs +1 -1
  18. package/dist/modules/animation/index.js +1 -1
  19. package/dist/modules/core/clock.cjs +1 -1
  20. package/dist/modules/core/clock.js +1 -1
  21. package/dist/modules/core/colors.cjs +1 -1
  22. package/dist/modules/core/colors.js +1 -1
  23. package/dist/modules/core/consts.cjs +3 -9
  24. package/dist/modules/core/consts.d.ts +1 -5
  25. package/dist/modules/core/consts.js +4 -8
  26. package/dist/modules/core/globals.cjs +16 -5
  27. package/dist/modules/core/globals.d.ts +22 -1
  28. package/dist/modules/core/globals.js +18 -6
  29. package/dist/modules/core/helpers.cjs +7 -10
  30. package/dist/modules/core/helpers.js +8 -11
  31. package/dist/modules/core/render.cjs +66 -63
  32. package/dist/modules/core/render.js +68 -65
  33. package/dist/modules/core/styles.cjs +53 -32
  34. package/dist/modules/core/styles.d.ts +1 -0
  35. package/dist/modules/core/styles.js +55 -35
  36. package/dist/modules/core/targets.cjs +1 -1
  37. package/dist/modules/core/targets.js +1 -1
  38. package/dist/modules/core/transforms.cjs +129 -13
  39. package/dist/modules/core/transforms.d.ts +1 -0
  40. package/dist/modules/core/transforms.js +130 -15
  41. package/dist/modules/core/units.cjs +1 -1
  42. package/dist/modules/core/units.js +1 -1
  43. package/dist/modules/core/values.cjs +68 -8
  44. package/dist/modules/core/values.d.ts +5 -2
  45. package/dist/modules/core/values.js +69 -11
  46. package/dist/modules/draggable/draggable.cjs +1 -1
  47. package/dist/modules/draggable/draggable.js +1 -1
  48. package/dist/modules/draggable/index.cjs +1 -1
  49. package/dist/modules/draggable/index.js +1 -1
  50. package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
  51. package/dist/modules/easings/cubic-bezier/index.js +1 -1
  52. package/dist/modules/easings/eases/index.cjs +1 -1
  53. package/dist/modules/easings/eases/index.js +1 -1
  54. package/dist/modules/easings/eases/parser.cjs +1 -1
  55. package/dist/modules/easings/eases/parser.js +1 -1
  56. package/dist/modules/easings/index.cjs +1 -1
  57. package/dist/modules/easings/index.js +1 -1
  58. package/dist/modules/easings/irregular/index.cjs +1 -1
  59. package/dist/modules/easings/irregular/index.js +1 -1
  60. package/dist/modules/easings/linear/index.cjs +1 -1
  61. package/dist/modules/easings/linear/index.js +1 -1
  62. package/dist/modules/easings/none.cjs +1 -1
  63. package/dist/modules/easings/none.js +1 -1
  64. package/dist/modules/easings/spring/index.cjs +1 -1
  65. package/dist/modules/easings/spring/index.js +1 -1
  66. package/dist/modules/easings/steps/index.cjs +1 -1
  67. package/dist/modules/easings/steps/index.js +1 -1
  68. package/dist/modules/engine/engine.cjs +1 -1
  69. package/dist/modules/engine/engine.js +1 -1
  70. package/dist/modules/engine/index.cjs +1 -1
  71. package/dist/modules/engine/index.js +1 -1
  72. package/dist/modules/events/index.cjs +1 -1
  73. package/dist/modules/events/index.js +1 -1
  74. package/dist/modules/events/scroll.cjs +1 -1
  75. package/dist/modules/events/scroll.js +1 -1
  76. package/dist/modules/index.cjs +9 -1
  77. package/dist/modules/index.d.ts +1 -0
  78. package/dist/modules/index.js +4 -1
  79. package/dist/modules/layout/index.cjs +1 -1
  80. package/dist/modules/layout/index.js +1 -1
  81. package/dist/modules/layout/layout.cjs +29 -25
  82. package/dist/modules/layout/layout.d.ts +4 -3
  83. package/dist/modules/layout/layout.js +30 -26
  84. package/dist/modules/scope/index.cjs +1 -1
  85. package/dist/modules/scope/index.js +1 -1
  86. package/dist/modules/scope/scope.cjs +1 -1
  87. package/dist/modules/scope/scope.js +1 -1
  88. package/dist/modules/svg/drawable.cjs +1 -1
  89. package/dist/modules/svg/drawable.js +1 -1
  90. package/dist/modules/svg/helpers.cjs +1 -1
  91. package/dist/modules/svg/helpers.js +1 -1
  92. package/dist/modules/svg/index.cjs +1 -1
  93. package/dist/modules/svg/index.js +1 -1
  94. package/dist/modules/svg/morphto.cjs +3 -6
  95. package/dist/modules/svg/morphto.js +3 -6
  96. package/dist/modules/svg/motionpath.cjs +1 -1
  97. package/dist/modules/svg/motionpath.js +1 -1
  98. package/dist/modules/text/index.cjs +3 -1
  99. package/dist/modules/text/index.d.ts +1 -0
  100. package/dist/modules/text/index.js +2 -1
  101. package/dist/modules/text/scramble.cjs +272 -0
  102. package/dist/modules/text/scramble.d.ts +3 -0
  103. package/dist/modules/text/scramble.js +270 -0
  104. package/dist/modules/text/split.cjs +5 -5
  105. package/dist/modules/text/split.d.ts +5 -5
  106. package/dist/modules/text/split.js +5 -5
  107. package/dist/modules/timeline/index.cjs +1 -1
  108. package/dist/modules/timeline/index.js +1 -1
  109. package/dist/modules/timeline/position.cjs +1 -1
  110. package/dist/modules/timeline/position.js +1 -1
  111. package/dist/modules/timeline/timeline.cjs +36 -18
  112. package/dist/modules/timeline/timeline.d.ts +6 -5
  113. package/dist/modules/timeline/timeline.js +37 -19
  114. package/dist/modules/timer/index.cjs +1 -1
  115. package/dist/modules/timer/index.js +1 -1
  116. package/dist/modules/timer/timer.cjs +8 -12
  117. package/dist/modules/timer/timer.d.ts +2 -0
  118. package/dist/modules/timer/timer.js +9 -13
  119. package/dist/modules/types/index.d.ts +76 -8
  120. package/dist/modules/utils/chainable.cjs +8 -5
  121. package/dist/modules/utils/chainable.js +8 -5
  122. package/dist/modules/utils/index.cjs +5 -1
  123. package/dist/modules/utils/index.d.ts +1 -0
  124. package/dist/modules/utils/index.js +2 -1
  125. package/dist/modules/utils/number.cjs +1 -1
  126. package/dist/modules/utils/number.js +1 -1
  127. package/dist/modules/utils/random.cjs +1 -1
  128. package/dist/modules/utils/random.js +1 -1
  129. package/dist/modules/utils/stagger.cjs +117 -20
  130. package/dist/modules/utils/stagger.js +118 -21
  131. package/dist/modules/utils/target.cjs +1 -1
  132. package/dist/modules/utils/target.js +1 -1
  133. package/dist/modules/utils/time.cjs +5 -3
  134. package/dist/modules/utils/time.d.ts +1 -1
  135. package/dist/modules/utils/time.js +5 -3
  136. package/dist/modules/waapi/composition.cjs +1 -1
  137. package/dist/modules/waapi/composition.js +1 -1
  138. package/dist/modules/waapi/index.cjs +1 -1
  139. package/dist/modules/waapi/index.js +1 -1
  140. package/dist/modules/waapi/waapi.cjs +19 -20
  141. package/dist/modules/waapi/waapi.js +20 -21
  142. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - ESM bundle
3
- * @version v4.3.6
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -46,7 +46,8 @@
46
46
  * @callback StaggerFunction
47
47
  * @param {Target} [target]
48
48
  * @param {Number} [index]
49
- * @param {Number} [length]
49
+ * @param {TargetsArray} [targets]
50
+ * @param {Tween|null} [prevTween]
50
51
  * @param {Timeline} [tl]
51
52
  * @return {T}
52
53
  */
@@ -54,9 +55,9 @@
54
55
  /**
55
56
  * @typedef {Object} StaggerParams
56
57
  * @property {Number|String} [start]
57
- * @property {Number|'first'|'center'|'last'|'random'} [from]
58
+ * @property {Number|'first'|'center'|'last'|'random'|Array.<Number>} [from]
58
59
  * @property {Boolean} [reversed]
59
- * @property {Array.<Number>} [grid]
60
+ * @property {Array.<Number>|Boolean} [grid]
60
61
  * @property {('x'|'y')} [axis]
61
62
  * @property {String|((target: Target, i: Number, length: Number) => Number)} [use]
62
63
  * @property {Number} [total]
@@ -117,8 +118,8 @@
117
118
 
118
119
  // A hack to get both ease names suggestions AND allow any strings
119
120
  // https://github.com/microsoft/TypeScript/issues/29729#issuecomment-460346421
120
- /** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring} EasingParam */
121
- /** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring} WAAPIEasingParam */
121
+ /** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring|TweakRegister} EasingParam */
122
+ /** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring|TweakRegister} WAAPIEasingParam */
122
123
 
123
124
  // Spring types
124
125
 
@@ -174,6 +175,7 @@
174
175
  * @property {Boolean|ScrollObserver} [autoplay]
175
176
  * @property {Number} [frameRate]
176
177
  * @property {Number} [playbackRate]
178
+ * @property {Number} [priority]
177
179
  */
178
180
 
179
181
  /**
@@ -184,9 +186,10 @@
184
186
 
185
187
  /**
186
188
  * @callback FunctionValue
187
- * @param {Target} target - The animated target
188
- * @param {Number} index - The target index
189
- * @param {Number} length - The total number of animated targets
189
+ * @param {Target} [target] - The animated target
190
+ * @param {Number} [index] - The target index
191
+ * @param {TargetsArray} [targets] - The array of all animated targets
192
+ * @param {Tween|null} [prevTween] - The previous sibling tween for the same target and property
190
193
  * @return {Number|String|TweenObjectValue|EasingParam|Array.<Number|String|TweenObjectValue>}
191
194
  */
192
195
 
@@ -355,7 +358,7 @@
355
358
  * - `'label'` - Label: Position animation at a named label position (e.g., `'My Label'`)<br>
356
359
  * - `stagger(String|Nummber)` - Stagger multi-elements animation positions (e.g., 10, 20, 30...)
357
360
  *
358
- * @typedef {TimelinePosition | StaggerFunction<Number|String>} TimelineAnimationPosition
361
+ * @typedef {TimelinePosition | StaggerFunction<Number|String> | TweakRegister} TimelineAnimationPosition
359
362
  */
360
363
 
361
364
  /**
@@ -379,7 +382,7 @@
379
382
  * @callback WAAPIFunctionValue
380
383
  * @param {DOMTarget} target - The animated target
381
384
  * @param {Number} index - The target index
382
- * @param {Number} length - The total number of animated targets
385
+ * @param {DOMTargetsArray} targets - The array of all animated targets
383
386
  * @return {WAAPITweenValue|WAAPIEasingParam}
384
387
  */
385
388
 
@@ -626,6 +629,26 @@
626
629
  * @property {Boolean} [debug]
627
630
  */
628
631
 
632
+ /**
633
+ * @typedef {Object} ScrambleTextParams
634
+ * @property {String|function(Target, Number, TargetsArray): String} [text] - the text to transition to, otherwise uses the original text
635
+ * @property {String|function(Target, Number, TargetsArray): String} [chars] - the characters used for scramble; named sets: 'lowercase', 'uppercase', 'numbers', 'symbols', 'braille', 'blocks', 'shades'; range syntax: 'A-Z', 'a-z0-9'; defaults to 'a-zA-Z0-9!%#_'
636
+ * @property {EasingParam} [ease] - the easing applied to the scramble animation
637
+ * @property {Number|'left'|'center'|'right'|'random'|'auto'} [from] - where the reveal wave starts from, 'auto' (default) uses 'left' when text grows and 'right' when it shrinks
638
+ * @property {Boolean} [reversed] - reverses the reveal order, so 'center' reveals from edges inward instead of center outward
639
+ * @property {Boolean|Number|String} [cursor] - characters displayed at the leading edge of the reveal wave; true uses '_', a number is a char code, a string is used directly
640
+ * @property {Number} [perturbation] - adds random timing offsets to each character's start and end, creating a more organic reveal
641
+ * @property {Number} [seed] - a seed for the random number generator to produce reproducible scramble sequences
642
+ * @property {Boolean|String} [override] - controls the starting appearance: false shows original text, true scrambles it (default), '' starts from blank, ' ' replaces characters with spaces, a custom string (supports range syntax like 'A-Z') uses its characters as scramble set
643
+ * @property {Number} [revealRate] - characters per second entering the active zone; higher values make the reveal wave move faster (default: 60)
644
+ * @property {Number} [settleDuration] - time in ms each character spends scrambling before settling into its final glyph (default: 300)
645
+ * @property {Number} [settleRate] - how many times per second scramble characters cycle in the active zone (default: 30)
646
+ * @property {Number|function(Target, Number, TargetsArray): Number} [duration] - if set to a value greater than 0, overrides the computed duration from interval and settle; if unset or 0, duration is calculated automatically from text length and timing parameters
647
+ * @property {Number|function(Target, Number, TargetsArray): Number} [revealDelay] - delay in ms before the reveal wave starts within the scramble animation
648
+ * @property {Number|function(Target, Number, TargetsArray): Number} [delay] - delay in ms before the entire scramble animation starts
649
+ * @property {function(String, Number): void} [onChange] - callback fired each time a character changes during scramble; receives the current scrambled text and the eased progress (0-1)
650
+ */
651
+
629
652
  // SVG types
630
653
 
631
654
  /**
@@ -641,7 +664,7 @@
641
664
  // TODO: Do we need to check if we're running inside a worker ?
642
665
  const isBrowser = typeof window !== 'undefined';
643
666
 
644
- /** @typedef {Window & {AnimeJS: Array} & {AnimeJSDevTools: any}|null} AnimeJSWindow
667
+ /** @typedef {Window & {AnimeJS: Array}|null} AnimeJSWindow
645
668
 
646
669
  /** @type {AnimeJSWindow} */
647
670
  const win = isBrowser ? /** @type {AnimeJSWindow} */(/** @type {unknown} */(window)) : null;
@@ -688,7 +711,6 @@ const isRegisteredTargetSymbol = Symbol();
688
711
  const isDomSymbol = Symbol();
689
712
  const isSvgSymbol = Symbol();
690
713
  const transformsSymbol = Symbol();
691
- const morphPointsSymbol = Symbol();
692
714
  const proxyTargetSymbol = Symbol();
693
715
 
694
716
  // Numbers
@@ -712,6 +734,7 @@ const shortTransforms = /*#__PURE__*/ (() => {
712
734
  })();
713
735
 
714
736
  const validTransforms = [
737
+ 'perspective',
715
738
  'translateX',
716
739
  'translateY',
717
740
  'translateZ',
@@ -726,9 +749,6 @@ const validTransforms = [
726
749
  'skew',
727
750
  'skewX',
728
751
  'skewY',
729
- 'matrix',
730
- 'matrix3d',
731
- 'perspective',
732
752
  ];
733
753
 
734
754
  const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({...a, [v]: v + '('}), {});
@@ -751,12 +771,23 @@ const digitWithExponentRgx = /[-+]?\d*\.?\d+(?:e[-+]?\d)?/gi;
751
771
  // export const unitsExecRgx = /^([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)+([a-z]+|%)$/i;
752
772
  const unitsExecRgx = /^([-+]?\d*\.?\d+(?:e[-+]?\d+)?)([a-z]+|%)$/i;
753
773
  const lowerCaseRgx = /([a-z])([A-Z])/g;
754
- const transformsExecRgx = /(\w+)(\([^)]+\)+)/g; // Match inline transforms with cacl() values, returns the value wrapped in ()
755
774
  const relativeValuesExecRgx = /(\*=|\+=|-=)/;
756
775
  const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/;
757
776
 
758
777
 
759
778
 
779
+ /**
780
+ * @typedef {Object} EditorGlobals
781
+ * @property {boolean} showPanel
782
+ * @property {boolean} synced
783
+ * @property {Function} addAnimation
784
+ * @property {Function} addTimeline
785
+ * @property {Function} addTimelineChild
786
+ * @property {Function} resolveStagger
787
+ * @property {Object|null} _head
788
+ * @property {Object|null} _tail
789
+ */
790
+
760
791
  /** @type {DefaultsParams} */
761
792
  const defaults = {
762
793
  id: null,
@@ -800,11 +831,11 @@ const globals = {
800
831
  timeScale: 1,
801
832
  /** @type {Number} */
802
833
  tickThreshold: 200,
834
+ /** @type {EditorGlobals|null} */
835
+ editor: null,
803
836
  };
804
837
 
805
- const devTools = isBrowser && win.AnimeJSDevTools;
806
-
807
- const globalVersions = { version: '4.3.6', engine: null };
838
+ const globalVersions = { version: '4.4.0', engine: null };
808
839
 
809
840
  if (isBrowser) {
810
841
  if (!win.AnimeJS) win.AnimeJS = [];
@@ -920,8 +951,6 @@ const _round = Math.round;
920
951
  */
921
952
  const clamp$1 = (v, min, max) => v < min ? min : v > max ? max : v;
922
953
 
923
- const powCache = {};
924
-
925
954
  /**
926
955
  * Rounds a number to specified decimal places
927
956
  *
@@ -929,13 +958,12 @@ const powCache = {};
929
958
  * @param {Number} decimalLength - Number of decimal places
930
959
  * @return {Number}
931
960
  */
932
- const round$1 = (v, decimalLength) => {
933
- if (decimalLength < 0) return v;
934
- if (!decimalLength) return _round(v);
935
- let p = powCache[decimalLength];
936
- if (!p) p = powCache[decimalLength] = 10 ** decimalLength;
937
- return _round(v * p) / p;
938
- };
961
+ const round$1 = (v, decimalLength) => {
962
+ if (decimalLength < 0) return v;
963
+ if (!decimalLength) return _round(v);
964
+ const p = 10 ** decimalLength;
965
+ return _round(v * p) / p;
966
+ };
939
967
 
940
968
  /**
941
969
  * Snaps a value to nearest increment or array value
@@ -1066,28 +1094,143 @@ const addChild = (parent, child, sortMethod, prevProp = '_prev', nextProp = '_ne
1066
1094
  */
1067
1095
  const parseInlineTransforms = (target, propName, animationInlineStyles) => {
1068
1096
  const inlineTransforms = target.style.transform;
1069
- let inlinedStylesPropertyValue;
1070
1097
  if (inlineTransforms) {
1071
1098
  const cachedTransforms = target[transformsSymbol];
1072
- let t; while (t = transformsExecRgx.exec(inlineTransforms)) {
1073
- const inlinePropertyName = t[1];
1074
- // const inlinePropertyValue = t[2];
1075
- const inlinePropertyValue = t[2].slice(1, -1);
1076
- cachedTransforms[inlinePropertyName] = inlinePropertyValue;
1077
- if (inlinePropertyName === propName) {
1078
- inlinedStylesPropertyValue = inlinePropertyValue;
1079
- // Store the new parsed inline styles if animationInlineStyles is provided
1080
- if (animationInlineStyles) {
1081
- animationInlineStyles[propName] = inlinePropertyValue;
1099
+ let pos = 0;
1100
+ const len = inlineTransforms.length;
1101
+ let fullTranslateValue;
1102
+ while (pos < len) {
1103
+ // Skip whitespace
1104
+ while (pos < len && inlineTransforms.charCodeAt(pos) === 32) pos++;
1105
+ if (pos >= len) break;
1106
+ // Read function name
1107
+ const nameStart = pos;
1108
+ while (pos < len && inlineTransforms.charCodeAt(pos) !== 40) pos++;
1109
+ if (pos >= len) break;
1110
+ const name = inlineTransforms.substring(nameStart, pos);
1111
+ // Scan to closing paren, recording top-level comma positions
1112
+ let depth = 1;
1113
+ const valueStart = pos + 1;
1114
+ let c1 = -1, c2 = -1;
1115
+ pos++;
1116
+ while (pos < len && depth > 0) {
1117
+ const c = inlineTransforms.charCodeAt(pos);
1118
+ if (c === 40) depth++;
1119
+ else if (c === 41) depth--;
1120
+ else if (c === 44 && depth === 1) {
1121
+ if (c1 === -1) c1 = pos;
1122
+ else if (c2 === -1) c2 = pos;
1123
+ }
1124
+ pos++;
1125
+ }
1126
+ const valueEnd = pos - 1;
1127
+ // Decompose multi-arg functions into individual axis properties
1128
+ if (name === 'translate' || name === 'translate3d') {
1129
+ if (c1 === -1) {
1130
+ cachedTransforms.translateX = inlineTransforms.substring(valueStart, valueEnd).trim();
1131
+ } else {
1132
+ cachedTransforms.translateX = inlineTransforms.substring(valueStart, c1).trim();
1133
+ if (c2 === -1) {
1134
+ cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
1135
+ } else {
1136
+ cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, c2).trim();
1137
+ cachedTransforms.translateZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
1138
+ }
1082
1139
  }
1140
+ fullTranslateValue = inlineTransforms.substring(valueStart, valueEnd);
1141
+ } else if (name === 'scale' || name === 'scale3d') {
1142
+ if (c1 === -1) {
1143
+ cachedTransforms.scale = inlineTransforms.substring(valueStart, valueEnd).trim();
1144
+ } else {
1145
+ cachedTransforms.scaleX = inlineTransforms.substring(valueStart, c1).trim();
1146
+ if (c2 === -1) {
1147
+ cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
1148
+ } else {
1149
+ cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, c2).trim();
1150
+ cachedTransforms.scaleZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
1151
+ }
1152
+ }
1153
+ } else {
1154
+ cachedTransforms[name] = inlineTransforms.substring(valueStart, valueEnd);
1083
1155
  }
1084
1156
  }
1157
+ // Resolve the requested property from the cache
1158
+ if (propName === 'translate3d' && fullTranslateValue) {
1159
+ if (animationInlineStyles) animationInlineStyles[propName] = fullTranslateValue;
1160
+ return fullTranslateValue;
1161
+ }
1162
+ const cached = cachedTransforms[propName];
1163
+ if (!isUnd(cached)) {
1164
+ if (animationInlineStyles) animationInlineStyles[propName] = cached;
1165
+ return cached;
1166
+ }
1085
1167
  }
1086
- return inlineTransforms && !isUnd(inlinedStylesPropertyValue) ? inlinedStylesPropertyValue :
1168
+ return propName === 'translate3d' ? '0px, 0px, 0px' :
1169
+ propName === 'rotate3d' ? '0, 0, 0, 0deg' :
1087
1170
  stringStartsWith(propName, 'scale') ? '1' :
1088
1171
  stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew') ? '0deg' : '0px';
1089
1172
  };
1090
1173
 
1174
+ /**
1175
+ * Builds a CSS transform string from the target's cached transform properties.
1176
+ * Iterates validTransforms in order (perspective > translate > rotate > scale > skew > matrix).
1177
+ * When adjacent axis properties are all present, emits a shorter shorthand (translateX + translateY -> translate(x, y))
1178
+ * The index is advanced past consumed properties so they are not emitted twice.
1179
+ * Properties without a grouping partner (e.g. translateY alone, scaleZ alone) emit individually.
1180
+ *
1181
+ * @param {Record<String, String>} props
1182
+ * @return {String}
1183
+ */
1184
+ const buildTransformString = (props) => {
1185
+ let str = emptyString;
1186
+ for (let i = 0, l = validTransforms.length; i < l; i++) {
1187
+ const key = validTransforms[i];
1188
+ const val = props[key];
1189
+ if (val !== undefined) {
1190
+ // Group translateX with adjacent translateY / translateZ
1191
+ if (key === 'translateX') {
1192
+ const next = props.translateY;
1193
+ if (next !== undefined) {
1194
+ const next2 = props.translateZ;
1195
+ if (next2 !== undefined) {
1196
+ str += `translate3d(${val},${next},${next2}) `;
1197
+ i += 2;
1198
+ } else {
1199
+ str += `translate(${val},${next}) `;
1200
+ i += 1;
1201
+ }
1202
+ continue;
1203
+ }
1204
+ }
1205
+ // Group scaleX with adjacent scaleY / scaleZ (only when standalone scale is absent)
1206
+ if (key === 'scaleX' && props.scale === undefined) {
1207
+ const next = props.scaleY;
1208
+ if (next !== undefined) {
1209
+ const next2 = props.scaleZ;
1210
+ if (next2 !== undefined) {
1211
+ str += `scale3d(${val},${next},${next2}) `;
1212
+ i += 2;
1213
+ } else {
1214
+ str += `scale(${val},${next}) `;
1215
+ i += 1;
1216
+ }
1217
+ continue;
1218
+ }
1219
+ }
1220
+ // All other properties: emit individually using pre-built fragment string
1221
+ str += `${transformsFragmentStrings[key]}${val}) `;
1222
+ }
1223
+ // Preserve non-animatable rotate3d in correct position (after rotateZ, before scale)
1224
+ if (key === 'rotateZ') {
1225
+ if (props.rotate3d !== undefined) str += `rotate3d(${props.rotate3d}) `;
1226
+ }
1227
+ }
1228
+ // Preserve non-animatable matrix/matrix3d from inline styles
1229
+ if (props.matrix !== undefined) str += `matrix(${props.matrix}) `;
1230
+ if (props.matrix3d !== undefined) str += `matrix3d(${props.matrix3d}) `;
1231
+ return str;
1232
+ };
1233
+
1091
1234
 
1092
1235
 
1093
1236
  /**
@@ -1189,15 +1332,16 @@ const setValue = (targetValue, defaultValue) => {
1189
1332
  * @param {TweenPropValue} value
1190
1333
  * @param {Target} target
1191
1334
  * @param {Number} index
1192
- * @param {Number} total
1193
- * @param {Object} [store]
1335
+ * @param {TargetsArray} targets
1336
+ * @param {Object|null} store
1337
+ * @param {Tween|null} prevTween
1194
1338
  * @return {any}
1195
1339
  */
1196
- const getFunctionValue = (value, target, index, total, store) => {
1340
+ const getFunctionValue = (value, target, index, targets, store, prevTween) => {
1197
1341
  let func;
1198
1342
  if (isFnc(value)) {
1199
1343
  func = () => {
1200
- const computed = /** @type {Function} */(value)(target, index, total);
1344
+ const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
1201
1345
  // Fallback to 0 if the function returns undefined / NaN / null / false / 0
1202
1346
  return !isNaN(+computed) ? +computed : computed || 0;
1203
1347
  };
@@ -1264,9 +1408,17 @@ const getCSSValue = (target, propName, animationInlineStyles) => {
1264
1408
  */
1265
1409
  const getOriginalAnimatableValue = (target, propName, tweenType, animationInlineStyles) => {
1266
1410
  const type = !isUnd(tweenType) ? tweenType : getTweenType(target, propName);
1267
- return type === tweenTypes.OBJECT ? target[propName] || 0 :
1268
- type === tweenTypes.ATTRIBUTE ? /** @type {DOMTarget} */(target).getAttribute(propName) :
1269
- type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
1411
+ if (type === tweenTypes.OBJECT) {
1412
+ const value = target[propName];
1413
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1414
+ return value || 0;
1415
+ }
1416
+ if (type === tweenTypes.ATTRIBUTE) {
1417
+ const value = /** @type {DOMTarget} */(target).getAttribute(propName);
1418
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1419
+ return value;
1420
+ }
1421
+ return type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
1270
1422
  type === tweenTypes.CSS_VAR ? getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles).trimStart() :
1271
1423
  getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles);
1272
1424
  };
@@ -1368,6 +1520,54 @@ const decomposeTweenValue = (tween, targetObject) => {
1368
1520
 
1369
1521
  const decomposedOriginalValue = createDecomposedValueTargetObject();
1370
1522
 
1523
+ /**
1524
+ * @param {Tween} tween
1525
+ * @param {Number} progress
1526
+ * @param {Number} precision
1527
+ * @return {String}
1528
+ */
1529
+ const composeColorValue = (tween, progress, precision) => {
1530
+ const mod = tween._modifier;
1531
+ const fn = tween._fromNumbers;
1532
+ const tn = tween._toNumbers;
1533
+ const r = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[0], tn[0], progress))), 0, 255), 0);
1534
+ const g = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[1], tn[1], progress))), 0, 255), 0);
1535
+ const b = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[2], tn[2], progress))), 0, 255), 0);
1536
+ const a = clamp$1(/** @type {Number} */(mod(round$1(lerp$1(fn[3], tn[3], progress), precision))), 0, 1);
1537
+ if (tween._composition !== compositionTypes.none) {
1538
+ const ns = tween._numbers;
1539
+ ns[0] = r;
1540
+ ns[1] = g;
1541
+ ns[2] = b;
1542
+ ns[3] = a;
1543
+ }
1544
+ return `rgba(${r},${g},${b},${a})`;
1545
+ };
1546
+
1547
+ /**
1548
+ * @param {Tween} tween
1549
+ * @param {Number} progress
1550
+ * @param {Number} precision
1551
+ * @return {String}
1552
+ */
1553
+ const composeComplexValue = (tween, progress, precision) => {
1554
+ const mod = tween._modifier;
1555
+ const fn = tween._fromNumbers;
1556
+ const tn = tween._toNumbers;
1557
+ const ts = tween._strings;
1558
+ const hasComposition = tween._composition !== compositionTypes.none;
1559
+ let v = ts[0];
1560
+ for (let j = 0, l = tn.length; j < l; j++) {
1561
+ const n = /** @type {Number} */(mod(round$1(lerp$1(fn[j], tn[j], progress), precision)));
1562
+ const s = ts[j + 1];
1563
+ v += `${s ? n + s : n}`;
1564
+ if (hasComposition) {
1565
+ tween._numbers[j] = n;
1566
+ }
1567
+ }
1568
+ return v;
1569
+ };
1570
+
1371
1571
 
1372
1572
 
1373
1573
 
@@ -1396,7 +1596,6 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1396
1596
  const _hasChildren = tickable._hasChildren;
1397
1597
  const tickableDelay = tickable._delay;
1398
1598
  const tickablePrevAbsoluteTime = tickable._currentTime; // TODO: rename ._currentTime to ._absoluteCurrentTime
1399
-
1400
1599
  const tickableEndTime = tickableDelay + iterationDuration;
1401
1600
  const tickableAbsoluteTime = time - tickableDelay;
1402
1601
  const tickablePrevTime = clamp$1(tickablePrevAbsoluteTime, -tickableDelay, duration);
@@ -1528,30 +1727,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1528
1727
  number = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
1529
1728
  value = `${number}${tween._unit}`;
1530
1729
  } else if (tweenValueType === valueTypes.COLOR) {
1531
- const fn = tween._fromNumbers;
1532
- const tn = tween._toNumbers;
1533
- const r = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[0], tn[0], tweenProgress))), 0, 255), 0);
1534
- const g = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[1], tn[1], tweenProgress))), 0, 255), 0);
1535
- const b = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[2], tn[2], tweenProgress))), 0, 255), 0);
1536
- const a = clamp$1(/** @type {Number} */(tweenModifier(round$1(lerp$1(fn[3], tn[3], tweenProgress), tweenPrecision))), 0, 1);
1537
- value = `rgba(${r},${g},${b},${a})`;
1538
- if (tweenHasComposition) {
1539
- const ns = tween._numbers;
1540
- ns[0] = r;
1541
- ns[1] = g;
1542
- ns[2] = b;
1543
- ns[3] = a;
1544
- }
1730
+ value = composeColorValue(tween, tweenProgress, tweenPrecision);
1545
1731
  } else if (tweenValueType === valueTypes.COMPLEX) {
1546
- value = tween._strings[0];
1547
- for (let j = 0, l = tween._toNumbers.length; j < l; j++) {
1548
- const n = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumbers[j], tween._toNumbers[j], tweenProgress), tweenPrecision)));
1549
- const s = tween._strings[j + 1];
1550
- value += `${s ? n + s : n}`;
1551
- if (tweenHasComposition) {
1552
- tween._numbers[j] = n;
1553
- }
1554
- }
1732
+ value = composeComplexValue(tween, tweenProgress, tweenPrecision);
1555
1733
  }
1556
1734
 
1557
1735
  // For additive tweens and Animatables
@@ -1594,14 +1772,8 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1594
1772
 
1595
1773
  }
1596
1774
 
1597
- // NOTE: Possible improvement: Use translate(x,y) / translate3d(x,y,z) syntax
1598
- // to reduce memory usage on string composition
1599
1775
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
1600
- let str = emptyString;
1601
- for (let key in tweenTargetTransformsProperties) {
1602
- str += `${transformsFragmentStrings[key]}${tweenTargetTransformsProperties[key]}) `;
1603
- }
1604
- tweenStyle.transform = str;
1776
+ tweenStyle.transform = buildTransformString(tweenTargetTransformsProperties);
1605
1777
  tweenTransformsNeedUpdate = 0;
1606
1778
  }
1607
1779
 
@@ -1658,6 +1830,50 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1658
1830
  return hasRendered;
1659
1831
  };
1660
1832
 
1833
+ // Shared context for extracted forEachChildren callbacks in tick()
1834
+ // Avoids closure allocation every frame
1835
+
1836
+ let renderCtxChildrenTime = 0;
1837
+ let renderCtxTlFps = 0;
1838
+ let renderCtxTickTime = 0;
1839
+ let renderCtxTickMode = 0;
1840
+ let renderCtxMuteCallbacks = 0;
1841
+ let renderCtxInternalRender = 0;
1842
+ let renderCtxChildrenHasRendered = 0;
1843
+ let renderCtxChildrenHaveCompleted = true;
1844
+ let loopCtxIsRunningBackwards = false;
1845
+ let loopCtxIterationDuration = 0;
1846
+ let loopCtxMuteCallbacks = 0;
1847
+
1848
+ /** @param {JSAnimation} child */
1849
+ const tickLoopChild = (child) => {
1850
+ if (!loopCtxIsRunningBackwards) {
1851
+ // Force an internal render to trigger the callbacks if the child has not completed on loop
1852
+ if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
1853
+ render(child, loopCtxIterationDuration, loopCtxMuteCallbacks, 1, tickModes.FORCE);
1854
+ }
1855
+ // Reset their began and completed flags to allow retrigering callbacks on the next iteration
1856
+ child.began = false;
1857
+ child.completed = false;
1858
+ } else {
1859
+ const childDuration = child.duration;
1860
+ const childStartTime = child._offset + child._delay;
1861
+ const childEndTime = childStartTime + childDuration;
1862
+ // Triggers the onComplete callback on reverse for children on the edges of the timeline
1863
+ if (!loopCtxMuteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === loopCtxIterationDuration)) {
1864
+ child.onComplete(child);
1865
+ }
1866
+ }
1867
+ };
1868
+
1869
+ /** @param {JSAnimation} child */
1870
+ const tickRenderChild = (child) => {
1871
+ const childTime = round$1((renderCtxChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
1872
+ const childTickMode = child._fps < renderCtxTlFps ? child.requestTick(renderCtxTickTime) : renderCtxTickMode;
1873
+ renderCtxChildrenHasRendered += render(child, childTime, renderCtxMuteCallbacks, renderCtxInternalRender, childTickMode);
1874
+ if (!child.completed && renderCtxChildrenHaveCompleted) renderCtxChildrenHaveCompleted = false;
1875
+ };
1876
+
1661
1877
  /**
1662
1878
  * @param {Tickable} tickable
1663
1879
  * @param {Number} time
@@ -1674,45 +1890,30 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1674
1890
  const tlIsRunningBackwards = tl.backwards;
1675
1891
  const tlChildrenTime = internalRender ? time : tl._iterationTime;
1676
1892
  const tlCildrenTickTime = now();
1677
-
1678
1893
  let tlChildrenHasRendered = 0;
1679
1894
  let tlChildrenHaveCompleted = true;
1680
-
1681
1895
  // If the timeline has looped forward, we need to manually triggers children skipped callbacks
1682
1896
  if (!internalRender && tl._currentIteration !== _currentIteration) {
1683
1897
  const tlIterationDuration = tl.iterationDuration;
1684
- forEachChildren(tl, (/** @type {JSAnimation} */child) => {
1685
- if (!tlIsRunningBackwards) {
1686
- // Force an internal render to trigger the callbacks if the child has not completed on loop
1687
- if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
1688
- render(child, tlIterationDuration, muteCallbacks, 1, tickModes.FORCE);
1689
- }
1690
- // Reset their began and completed flags to allow retrigering callbacks on the next iteration
1691
- child.began = false;
1692
- child.completed = false;
1693
- } else {
1694
- const childDuration = child.duration;
1695
- const childStartTime = child._offset + child._delay;
1696
- const childEndTime = childStartTime + childDuration;
1697
- // Triggers the onComplete callback on reverse for children on the edges of the timeline
1698
- if (!muteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === tlIterationDuration)) {
1699
- child.onComplete(child);
1700
- }
1701
- }
1702
- });
1898
+ loopCtxIsRunningBackwards = tlIsRunningBackwards;
1899
+ loopCtxIterationDuration = tlIterationDuration;
1900
+ loopCtxMuteCallbacks = muteCallbacks;
1901
+ forEachChildren(tl, tickLoopChild);
1703
1902
  if (!muteCallbacks) tl.onLoop(/** @type {CallbackArgument} */(tl));
1704
1903
  }
1705
-
1706
- forEachChildren(tl, (/** @type {JSAnimation} */child) => {
1707
- const childTime = round$1((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
1708
- const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
1709
- tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
1710
- if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
1711
- }, tlIsRunningBackwards);
1712
-
1904
+ renderCtxChildrenTime = tlChildrenTime;
1905
+ renderCtxTlFps = tl._fps;
1906
+ renderCtxTickTime = tlCildrenTickTime;
1907
+ renderCtxTickMode = tickMode;
1908
+ renderCtxMuteCallbacks = muteCallbacks;
1909
+ renderCtxInternalRender = internalRender;
1910
+ renderCtxChildrenHasRendered = 0;
1911
+ renderCtxChildrenHaveCompleted = true;
1912
+ forEachChildren(tl, tickRenderChild, tlIsRunningBackwards);
1913
+ tlChildrenHasRendered = renderCtxChildrenHasRendered;
1914
+ tlChildrenHaveCompleted = renderCtxChildrenHaveCompleted;
1713
1915
  // Renders on timeline are triggered by its children so it needs to be set after rendering the children
1714
1916
  if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
1715
-
1716
1917
  // Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
1717
1918
  if ((tlChildrenHaveCompleted || tlIsRunningBackwards) && tl._currentTime >= tl.duration) {
1718
1919
  // Make sure the paused flag is false in case it has been skipped in the render function
@@ -1766,59 +1967,78 @@ const sanitizePropertyName = (propertyName, target, tweenType) => {
1766
1967
  /**
1767
1968
  * @template {Renderable} T
1768
1969
  * @param {T} renderable
1970
+ * @param {Boolean} [inlineStylesOnly]
1769
1971
  * @return {T}
1770
1972
  */
1771
- const cleanInlineStyles = renderable => {
1772
- // Allow cleanInlineStyles() to be called on timelines
1973
+ const revertValues = (renderable, inlineStylesOnly = false) => {
1974
+ // Allow revertValues() to be called on timelines
1773
1975
  if (renderable._hasChildren) {
1774
- forEachChildren(renderable, cleanInlineStyles, true);
1976
+ forEachChildren(renderable, (/** @type {Renderable} */child) => revertValues(child, inlineStylesOnly), true);
1775
1977
  } else {
1776
1978
  const animation = /** @type {JSAnimation} */(renderable);
1777
1979
  animation.pause();
1778
1980
  forEachChildren(animation, (/** @type {Tween} */tween) => {
1779
1981
  const tweenProperty = tween.property;
1780
1982
  const tweenTarget = tween.target;
1781
- if (tweenTarget[isDomSymbol]) {
1782
- const targetStyle = /** @type {DOMTarget} */(tweenTarget).style;
1783
- const originalInlinedValue = tween._inlineValue;
1784
- const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1785
- if (tween._tweenType === tweenTypes.TRANSFORM) {
1786
- const cachedTransforms = tweenTarget[transformsSymbol];
1787
- if (tweenHadNoInlineValue) {
1788
- delete cachedTransforms[tweenProperty];
1789
- } else {
1790
- cachedTransforms[tweenProperty] = originalInlinedValue;
1791
- }
1792
- if (tween._renderTransforms) {
1793
- if (!Object.keys(cachedTransforms).length) {
1794
- targetStyle.removeProperty('transform');
1983
+ const tweenType = tween._tweenType;
1984
+ const originalInlinedValue = tween._inlineValue;
1985
+ const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1986
+ if (tweenType === tweenTypes.OBJECT) {
1987
+ if (!inlineStylesOnly && !tweenHadNoInlineValue) {
1988
+ tweenTarget[tweenProperty] = originalInlinedValue;
1989
+ }
1990
+ } else if (tweenTarget[isDomSymbol]) {
1991
+ if (tweenType === tweenTypes.ATTRIBUTE) {
1992
+ if (!inlineStylesOnly) {
1993
+ if (tweenHadNoInlineValue) {
1994
+ /** @type {DOMTarget} */(tweenTarget).removeAttribute(tweenProperty);
1795
1995
  } else {
1796
- let str = emptyString;
1797
- for (let key in cachedTransforms) {
1798
- str += transformsFragmentStrings[key] + cachedTransforms[key] + ') ';
1799
- }
1800
- targetStyle.transform = str;
1996
+ /** @type {DOMTarget} */(tweenTarget).setAttribute(tweenProperty, /** @type {String} */(originalInlinedValue));
1801
1997
  }
1802
1998
  }
1803
1999
  } else {
1804
- if (tweenHadNoInlineValue) {
1805
- targetStyle.removeProperty(toLowerCase(tweenProperty));
2000
+ const targetStyle = /** @type {DOMTarget} */(tweenTarget).style;
2001
+ if (tweenType === tweenTypes.TRANSFORM) {
2002
+ const cachedTransforms = tweenTarget[transformsSymbol];
2003
+ if (tweenHadNoInlineValue) {
2004
+ delete cachedTransforms[tweenProperty];
2005
+ } else {
2006
+ cachedTransforms[tweenProperty] = originalInlinedValue;
2007
+ }
2008
+ if (tween._renderTransforms) {
2009
+ if (!Object.keys(cachedTransforms).length) {
2010
+ targetStyle.removeProperty('transform');
2011
+ } else {
2012
+ targetStyle.transform = buildTransformString(cachedTransforms);
2013
+ }
2014
+ }
1806
2015
  } else {
1807
- targetStyle[tweenProperty] = originalInlinedValue;
2016
+ if (tweenHadNoInlineValue) {
2017
+ targetStyle.removeProperty(toLowerCase(tweenProperty));
2018
+ } else {
2019
+ targetStyle[tweenProperty] = originalInlinedValue;
2020
+ }
1808
2021
  }
1809
2022
  }
1810
- if (animation._tail === tween) {
1811
- animation.targets.forEach(t => {
1812
- if (t.getAttribute && t.getAttribute('style') === emptyString) {
1813
- t.removeAttribute('style');
1814
- } });
1815
- }
2023
+ }
2024
+ if (tweenTarget[isDomSymbol] && animation._tail === tween) {
2025
+ animation.targets.forEach(t => {
2026
+ if (t.getAttribute && t.getAttribute('style') === emptyString) {
2027
+ t.removeAttribute('style');
2028
+ } });
1816
2029
  }
1817
2030
  });
1818
2031
  }
1819
2032
  return renderable;
1820
2033
  };
1821
2034
 
2035
+ /**
2036
+ * @template {Renderable} T
2037
+ * @param {T} renderable
2038
+ * @return {T}
2039
+ */
2040
+ const cleanInlineStyles = renderable => revertValues(renderable, true);
2041
+
1822
2042
 
1823
2043
 
1824
2044
  /*
@@ -2503,6 +2723,9 @@ const reviveTimer = timer => {
2503
2723
 
2504
2724
  let timerId = 0;
2505
2725
 
2726
+ /** @param {Timer} prev @param {Timer} child */
2727
+ const sortByPriority = (prev, child) => prev._priority > child._priority;
2728
+
2506
2729
  /**
2507
2730
  * Base class used to create Timers, Animations and Timelines
2508
2731
  */
@@ -2529,6 +2752,7 @@ class Timer extends Clock {
2529
2752
  autoplay,
2530
2753
  frameRate,
2531
2754
  playbackRate,
2755
+ priority,
2532
2756
  onComplete,
2533
2757
  onLoop,
2534
2758
  onPause,
@@ -2550,16 +2774,6 @@ class Timer extends Clock {
2550
2774
  /** @type {Number} */(timerLoop) < 0 ? Infinity :
2551
2775
  /** @type {Number} */(timerLoop) + 1;
2552
2776
 
2553
- if (devTools) {
2554
- const isInfinite = timerIterationCount === Infinity;
2555
- const registered = devTools.register(this, parameters, isInfinite);
2556
- if (registered && isInfinite) {
2557
- const minIterations = alternate ? 2 : 1;
2558
- const iterations = parent ? devTools.maxNestedInfiniteLoops : devTools.maxInfiniteLoops;
2559
- timerIterationCount = Math.max(iterations, minIterations);
2560
- }
2561
- }
2562
-
2563
2777
  let offsetPosition = 0;
2564
2778
 
2565
2779
  if (parent) {
@@ -2643,6 +2857,8 @@ class Timer extends Clock {
2643
2857
  this._fps = setValue(frameRate, timerDefaults.frameRate);
2644
2858
  /** @type {Number} */
2645
2859
  this._speed = setValue(playbackRate, timerDefaults.playbackRate);
2860
+ /** @type {Number} */
2861
+ this._priority = +setValue(priority, 1);
2646
2862
  }
2647
2863
 
2648
2864
  get cancelled() {
@@ -2787,7 +3003,7 @@ class Timer extends Clock {
2787
3003
  tick(this, minValue, 0, 0, tickModes.FORCE);
2788
3004
  } else {
2789
3005
  if (!this._running) {
2790
- addChild(engine, this);
3006
+ addChild(engine, this, sortByPriority);
2791
3007
  engine._hasChildren = true;
2792
3008
  this._running = true;
2793
3009
  }
@@ -3382,7 +3598,7 @@ class JSAnimation extends Timer {
3382
3598
  * @param {Number} [parentPosition]
3383
3599
  * @param {Boolean} [fastSet=false]
3384
3600
  * @param {Number} [index=0]
3385
- * @param {Number} [length=0]
3601
+ * @param {TargetsArray} [allTargets]
3386
3602
  */
3387
3603
  constructor(
3388
3604
  targets,
@@ -3391,7 +3607,7 @@ class JSAnimation extends Timer {
3391
3607
  parentPosition,
3392
3608
  fastSet = false,
3393
3609
  index = 0,
3394
- length = 0
3610
+ allTargets
3395
3611
  ) {
3396
3612
 
3397
3613
  super(/** @type {TimerParams & AnimationParams} */(parameters), parent, parentPosition);
@@ -3442,7 +3658,7 @@ class JSAnimation extends Timer {
3442
3658
 
3443
3659
  const target = parsedTargets[targetIndex];
3444
3660
  const ti = index || targetIndex;
3445
- const tl = length || targetsLength;
3661
+ const tl = allTargets || parsedTargets;
3446
3662
 
3447
3663
  let lastTransformGroupIndex = NaN;
3448
3664
  let lastTransformGroupLength = NaN;
@@ -3518,7 +3734,14 @@ class JSAnimation extends Timer {
3518
3734
  toFunctionStore.func = null;
3519
3735
  fromFunctionStore.func = null;
3520
3736
 
3521
- const computedToValue = getFunctionValue(key.to, target, ti, tl, toFunctionStore);
3737
+ const computedComposition = getFunctionValue(setValue(key.composition, tComposition), target, ti, tl, null, null);
3738
+ const tweenComposition = isNum(computedComposition) ? computedComposition : compositionTypes[computedComposition];
3739
+ if (!siblings && tweenComposition !== compositionTypes.none) siblings = getTweenSiblings(target, propName);
3740
+ // Timelines pass the last sibling tween if it belongs to the same timeline
3741
+ // Standalone animations only pass prevTween when the property has multiple keyframes
3742
+ const tailTween = siblings ? siblings._tail : null;
3743
+ const prevSiblingTween = parent && tailTween && tailTween.parent.parent === parent ? tailTween : prevTween;
3744
+ const computedToValue = getFunctionValue(key.to, target, ti, tl, toFunctionStore, prevSiblingTween);
3522
3745
 
3523
3746
  let tweenToValue;
3524
3747
  // Allows function based values to return an object syntax value ({to: v})
@@ -3528,20 +3751,18 @@ class JSAnimation extends Timer {
3528
3751
  } else {
3529
3752
  tweenToValue = computedToValue;
3530
3753
  }
3531
- const tweenFromValue = getFunctionValue(key.from, target, ti, tl);
3754
+ const tweenFromValue = getFunctionValue(key.from, target, ti, tl, null, prevSiblingTween);
3532
3755
  const easeToParse = key.ease || tEasing;
3533
3756
 
3534
- const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl);
3757
+ const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl, null, prevSiblingTween);
3535
3758
  const keyEasing = isFnc(easeFunctionResult) || isStr(easeFunctionResult) ? easeFunctionResult : easeToParse;
3536
3759
 
3537
3760
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
3538
3761
  const tweenEasing = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
3539
3762
  // Calculate default individual keyframe duration by dividing the tl of keyframes
3540
- const tweenDuration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(setValue(key.duration, (l > 1 ? getFunctionValue(tDuration, target, ti, tl) / l : tDuration)), target, ti, tl);
3763
+ 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);
3541
3764
  // Default delay value should only be applied to the first tween
3542
- const tweenDelay = getFunctionValue(setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl);
3543
- const computedComposition = getFunctionValue(setValue(key.composition, tComposition), target, ti, tl);
3544
- const tweenComposition = isNum(computedComposition) ? computedComposition : compositionTypes[computedComposition];
3765
+ const tweenDelay = getFunctionValue(setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl, null, prevSiblingTween);
3545
3766
  // Modifiers are treated differently and don't accept function based value to prevent having to pass a function wrapper
3546
3767
  const tweenModifier = key.modifier || tModifier;
3547
3768
  const hasFromvalue = !isUnd(tweenFromValue);
@@ -3558,7 +3779,6 @@ class JSAnimation extends Timer {
3558
3779
  let prevSibling = prevTween;
3559
3780
 
3560
3781
  if (tweenComposition !== compositionTypes.none) {
3561
- if (!siblings) siblings = getTweenSiblings(target, propName);
3562
3782
  let nextSibling = siblings._head;
3563
3783
  // Iterate trough all the next siblings until we find a sibling with an equal or inferior start time
3564
3784
  while (nextSibling && !nextSibling._isOverridden && nextSibling._absoluteStartTime <= absoluteStartTime) {
@@ -3577,8 +3797,8 @@ class JSAnimation extends Timer {
3577
3797
 
3578
3798
  // Decompose values
3579
3799
  if (isFromToValue) {
3580
- decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore) : tweenFromValue, fromTargetObject);
3581
- decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore) : tweenToValue, toTargetObject);
3800
+ decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore, prevSiblingTween) : tweenFromValue, fromTargetObject);
3801
+ decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore, prevSiblingTween) : tweenToValue, toTargetObject);
3582
3802
  // Needed to force an inline style registration
3583
3803
  const originalValue = getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore);
3584
3804
  if (fromTargetObject.t === valueTypes.NUMBER) {
@@ -3732,6 +3952,18 @@ class JSAnimation extends Timer {
3732
3952
  composeTween(tween, siblings);
3733
3953
  }
3734
3954
 
3955
+ // Pre-compute the tween end value for function-based value chaining (ie morphTo / scrambleText in keyframe arrays and timelines)
3956
+ const vt = tween._valueType;
3957
+ if (vt === valueTypes.COMPLEX) {
3958
+ tween._value = composeComplexValue(tween, 1, -1);
3959
+ } else if (vt === valueTypes.COLOR) {
3960
+ tween._value = composeColorValue(tween, 1, -1);
3961
+ } else if (vt === valueTypes.UNIT) {
3962
+ tween._value = `${tweenModifier(tween._toNumber)}${tween._unit}`;
3963
+ } else {
3964
+ tween._value = tweenModifier(tween._toNumber);
3965
+ }
3966
+
3735
3967
  if (isNaN(firstTweenChangeStartTime)) {
3736
3968
  firstTweenChangeStartTime = tween._startTime;
3737
3969
  }
@@ -3889,7 +4121,7 @@ class JSAnimation extends Timer {
3889
4121
  */
3890
4122
  revert() {
3891
4123
  super.revert();
3892
- return cleanInlineStyles(this);
4124
+ return revertValues(this);
3893
4125
  }
3894
4126
 
3895
4127
  /**
@@ -3911,170 +4143,12 @@ class JSAnimation extends Timer {
3911
4143
  * @param {AnimationParams} parameters
3912
4144
  * @return {JSAnimation}
3913
4145
  */
3914
- const animate = (targets, parameters) => new JSAnimation(targets, parameters, null, 0, false).init();
3915
-
3916
-
3917
-
3918
-
3919
-
3920
- const WAAPIAnimationsLookups = {
3921
- _head: null,
3922
- _tail: null,
3923
- };
3924
-
3925
- /**
3926
- * @param {DOMTarget} $el
3927
- * @param {String} [property]
3928
- * @param {WAAPIAnimation} [parent]
3929
- * @return {globalThis.Animation}
3930
- */
3931
- const removeWAAPIAnimation = ($el, property, parent) => {
3932
- let nextLookup = WAAPIAnimationsLookups._head;
3933
- let anim;
3934
- while (nextLookup) {
3935
- const next = nextLookup._next;
3936
- const matchTarget = nextLookup.$el === $el;
3937
- const matchProperty = !property || nextLookup.property === property;
3938
- const matchParent = !parent || nextLookup.parent === parent;
3939
- if (matchTarget && matchProperty && matchParent) {
3940
- anim = nextLookup.animation;
3941
- try { anim.commitStyles(); } catch {} anim.cancel();
3942
- removeChild(WAAPIAnimationsLookups, nextLookup);
3943
- const lookupParent = nextLookup.parent;
3944
- if (lookupParent) {
3945
- lookupParent._completed++;
3946
- if (lookupParent.animations.length === lookupParent._completed) {
3947
- lookupParent.completed = true;
3948
- lookupParent.paused = true;
3949
- if (!lookupParent.muteCallbacks) {
3950
- lookupParent.onComplete(lookupParent);
3951
- lookupParent._resolve(lookupParent);
3952
- }
3953
- }
3954
- }
3955
- }
3956
- nextLookup = next;
3957
- }
3958
- return anim;
3959
- };
3960
-
3961
- /**
3962
- * @param {WAAPIAnimation} parent
3963
- * @param {DOMTarget} $el
3964
- * @param {String} property
3965
- * @param {PropertyIndexedKeyframes} keyframes
3966
- * @param {KeyframeAnimationOptions} params
3967
- * @retun {globalThis.Animation}
3968
- */
3969
- const addWAAPIAnimation = (parent, $el, property, keyframes, params) => {
3970
- const animation = $el.animate(keyframes, params);
3971
- const animTotalDuration = params.delay + (+params.duration * params.iterations);
3972
- animation.playbackRate = parent._speed;
3973
- if (parent.paused) animation.pause();
3974
- if (parent.duration < animTotalDuration) {
3975
- parent.duration = animTotalDuration;
3976
- parent.controlAnimation = animation;
3977
- }
3978
- parent.animations.push(animation);
3979
- removeWAAPIAnimation($el, property);
3980
- addChild(WAAPIAnimationsLookups, { parent, animation, $el, property, _next: null, _prev: null });
3981
- const handleRemove = () => removeWAAPIAnimation($el, property, parent);
3982
- animation.oncancel = handleRemove;
3983
- animation.onremove = handleRemove;
3984
- if (!parent.persist) {
3985
- animation.onfinish = handleRemove;
3986
- }
3987
- return animation;
3988
- };
3989
-
3990
-
3991
-
3992
-
3993
-
3994
- /**
3995
- * @overload
3996
- * @param {DOMTargetSelector} targetSelector
3997
- * @param {String} propName
3998
- * @return {String}
3999
- *
4000
- * @overload
4001
- * @param {JSTargetsParam} targetSelector
4002
- * @param {String} propName
4003
- * @return {Number|String}
4004
- *
4005
- * @overload
4006
- * @param {DOMTargetsParam} targetSelector
4007
- * @param {String} propName
4008
- * @param {String} unit
4009
- * @return {String}
4010
- *
4011
- * @overload
4012
- * @param {TargetsParam} targetSelector
4013
- * @param {String} propName
4014
- * @param {Boolean} unit
4015
- * @return {Number}
4016
- *
4017
- * @param {TargetsParam} targetSelector
4018
- * @param {String} propName
4019
- * @param {String|Boolean} [unit]
4020
- */
4021
- function get(targetSelector, propName, unit) {
4022
- const targets = registerTargets(targetSelector);
4023
- if (!targets.length) return;
4024
- const [ target ] = targets;
4025
- const tweenType = getTweenType(target, propName);
4026
- const normalizePropName = sanitizePropertyName(propName, target, tweenType);
4027
- let originalValue = getOriginalAnimatableValue(target, normalizePropName);
4028
- if (isUnd(unit)) {
4029
- return originalValue;
4030
- } else {
4031
- decomposeRawValue(originalValue, decomposedOriginalValue);
4032
- if (decomposedOriginalValue.t === valueTypes.NUMBER || decomposedOriginalValue.t === valueTypes.UNIT) {
4033
- if (unit === false) {
4034
- return decomposedOriginalValue.n;
4035
- } else {
4036
- const convertedValue = convertValueUnit(/** @type {DOMTarget} */(target), decomposedOriginalValue, /** @type {String} */(unit), false);
4037
- return `${round$1(convertedValue.n, globals.precision)}${convertedValue.u}`;
4038
- }
4039
- }
4040
- }
4041
- }
4042
-
4043
- /**
4044
- * @param {TargetsParam} targets
4045
- * @param {AnimationParams} parameters
4046
- * @return {JSAnimation}
4047
- */
4048
- const set = (targets, parameters) => {
4049
- if (isUnd(parameters)) return;
4050
- parameters.duration = minValue;
4051
- // Do not overrides currently active tweens by default
4052
- parameters.composition = setValue(parameters.composition, compositionTypes.none);
4053
- // Skip init() and force rendering by playing the animation
4054
- return new JSAnimation(targets, parameters, null, 0, true).resume();
4055
- };
4056
-
4057
- /**
4058
- * @param {TargetsParam} targets
4059
- * @param {Renderable|WAAPIAnimation} [renderable]
4060
- * @param {String} [propertyName]
4061
- * @return {TargetsArray}
4062
- */
4063
- const remove = (targets, renderable, propertyName) => {
4064
- const targetsArray = parseTargets(targets);
4065
- for (let i = 0, l = targetsArray.length; i < l; i++) {
4066
- removeWAAPIAnimation(
4067
- /** @type {DOMTarget} */(targetsArray[i]),
4068
- propertyName,
4069
- renderable && /** @type {WAAPIAnimation} */(renderable).controlAnimation && /** @type {WAAPIAnimation} */(renderable),
4070
- );
4146
+ const animate = (targets, parameters) => {
4147
+ if (globals.editor) {
4148
+ return globals.editor.addAnimation(targets, parameters);
4149
+ } else {
4150
+ return new JSAnimation(targets, parameters, null, 0, false).init();
4071
4151
  }
4072
- removeTargetsFromRenderable(
4073
- targetsArray,
4074
- /** @type {Renderable} */(renderable),
4075
- propertyName
4076
- );
4077
- return targetsArray;
4078
4152
  };
4079
4153
 
4080
4154
 
@@ -4130,6 +4204,8 @@ const parseTimelinePosition = (timeline, timePosition) => {
4130
4204
 
4131
4205
 
4132
4206
 
4207
+
4208
+
4133
4209
  /**
4134
4210
  * @param {Timeline} tl
4135
4211
  * @return {Number}
@@ -4151,7 +4227,7 @@ function getTimelineTotalDuration(tl) {
4151
4227
  * @param {Number} timePosition
4152
4228
  * @param {TargetsParam} targets
4153
4229
  * @param {Number} [index]
4154
- * @param {Number} [length]
4230
+ * @param {TargetsArray} [allTargets]
4155
4231
  * @return {Timeline}
4156
4232
  *
4157
4233
  * @param {TimerParams|AnimationParams} childParams
@@ -4159,15 +4235,15 @@ function getTimelineTotalDuration(tl) {
4159
4235
  * @param {Number} timePosition
4160
4236
  * @param {TargetsParam} [targets]
4161
4237
  * @param {Number} [index]
4162
- * @param {Number} [length]
4238
+ * @param {TargetsArray} [allTargets]
4163
4239
  */
4164
- function addTlChild(childParams, tl, timePosition, targets, index, length) {
4240
+ function addTlChild(childParams, tl, timePosition, targets, index, allTargets) {
4165
4241
  const isSetter = isNum(childParams.duration) && /** @type {Number} */(childParams.duration) <= minValue;
4166
4242
  // Offset the tl position with -minValue for 0 duration animations or .set() calls in order to align their end value with the defined position
4167
4243
  const adjustedPosition = isSetter ? timePosition - minValue : timePosition;
4168
4244
  if (tl.composition) tick(tl, adjustedPosition, 1, 1, tickModes.AUTO);
4169
4245
  const tlChild = targets ?
4170
- new JSAnimation(targets,/** @type {AnimationParams} */(childParams), tl, adjustedPosition, false, index, length) :
4246
+ new JSAnimation(targets,/** @type {AnimationParams} */(childParams), tl, adjustedPosition, false, index, allTargets) :
4171
4247
  new Timer(/** @type {TimerParams} */(childParams), tl, adjustedPosition);
4172
4248
  if (tl.composition) tlChild.init(true);
4173
4249
  // TODO: Might be better to insert at a position relative to startTime?
@@ -4215,7 +4291,7 @@ class Timeline extends Timer {
4215
4291
  * @overload
4216
4292
  * @param {TargetsParam} a1
4217
4293
  * @param {AnimationParams} a2
4218
- * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4294
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [a3]
4219
4295
  * @return {this}
4220
4296
  *
4221
4297
  * @overload
@@ -4225,7 +4301,7 @@ class Timeline extends Timer {
4225
4301
  *
4226
4302
  * @param {TargetsParam|TimerParams} a1
4227
4303
  * @param {TimelinePosition|AnimationParams} a2
4228
- * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4304
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [a3]
4229
4305
  */
4230
4306
  add(a1, a2, a3) {
4231
4307
  const isAnim = isObj(a2);
@@ -4234,9 +4310,11 @@ class Timeline extends Timer {
4234
4310
  this._hasChildren = true;
4235
4311
  if (isAnim) {
4236
4312
  const childParams = /** @type {AnimationParams} */(a2);
4237
- // Check for function for children stagger positions
4238
- if (isFnc(a3)) {
4239
- const staggeredPosition = a3;
4313
+ const editorHook = globals.editor && globals.editor.addTimelineChild;
4314
+ const isStaggerType = a3 && /** @type {TweakRegister} */(a3).type === 'Stagger' && globals.editor;
4315
+ // Check for function or Stagger type children positions
4316
+ const staggeredPosition = isFnc(a3) ? a3 : null;
4317
+ if (staggeredPosition || isStaggerType) {
4240
4318
  const parsedTargetsArray = parseTargets(/** @type {TargetsParam} */(a1));
4241
4319
  // Store initial duration before adding new children that will change the duration
4242
4320
  const tlDuration = this.duration;
@@ -4247,28 +4325,36 @@ class Timeline extends Timer {
4247
4325
  let i = 0;
4248
4326
  /** @type {Number} */
4249
4327
  const parsedLength = (parsedTargetsArray.length);
4328
+ // Call editor hook once for the entire stagger group instead of per target
4329
+ const resolvedParams = editorHook ? editorHook(/** @type {TargetsParam} */(a1), childParams, this.id, a3, parsedLength) : null;
4330
+ // Resolve stagger AFTER editor hook so tweaked position value (a3.defaultValue) is used
4331
+ const staggerFn = staggeredPosition || globals.editor.resolveStagger(/** @type {TweakRegister} */(a3).defaultValue);
4250
4332
  parsedTargetsArray.forEach((/** @type {Target} */target) => {
4251
4333
  // Create a new parameter object for each staggered children
4252
- const staggeredChildParams = { ...childParams };
4334
+ const staggeredChildParams = { ...(resolvedParams || childParams) };
4253
4335
  // Reset the duration of the timeline iteration before each stagger to prevent wrong start value calculation
4254
4336
  this.duration = tlDuration;
4255
4337
  this.iterationDuration = tlIterationDuration;
4256
4338
  if (!isUnd(id)) staggeredChildParams.id = id + '-' + i;
4339
+ const staggeredTimePosition = parseTimelinePosition(this, staggerFn(target, i, parsedTargetsArray, null, this));
4257
4340
  addTlChild(
4258
4341
  staggeredChildParams,
4259
4342
  this,
4260
- parseTimelinePosition(this, staggeredPosition(target, i, parsedLength, this)),
4343
+ staggeredTimePosition,
4261
4344
  target,
4262
4345
  i,
4263
- parsedLength
4346
+ parsedTargetsArray,
4264
4347
  );
4265
4348
  i++;
4266
4349
  });
4267
4350
  } else {
4351
+ // Call editor hook before resolving position so tweaked values are applied
4352
+ const resolvedChildParams = editorHook ? editorHook(/** @type {TargetsParam} */(a1), childParams, this.id, a3) : childParams;
4353
+ const resolvedPosition = a3 && /** @type {*} */(a3).type ? /** @type {*} */(a3).defaultValue : a3;
4268
4354
  addTlChild(
4269
- childParams,
4355
+ resolvedChildParams,
4270
4356
  this,
4271
- parseTimelinePosition(this, a3),
4357
+ parseTimelinePosition(this, resolvedPosition),
4272
4358
  /** @type {TargetsParam} */(a1),
4273
4359
  );
4274
4360
  }
@@ -4390,7 +4476,7 @@ class Timeline extends Timer {
4390
4476
  revert() {
4391
4477
  super.revert();
4392
4478
  forEachChildren(this, (/** @type {JSAnimation|Timer} */child) => child.revert, true);
4393
- return cleanInlineStyles(this);
4479
+ return revertValues(this);
4394
4480
  }
4395
4481
 
4396
4482
  /**
@@ -4410,7 +4496,12 @@ class Timeline extends Timer {
4410
4496
  * @param {TimelineParams} [parameters]
4411
4497
  * @return {Timeline}
4412
4498
  */
4413
- const createTimeline = parameters => new Timeline(parameters).init();
4499
+ const createTimeline = parameters => {
4500
+ if (globals.editor) {
4501
+ return /** @type {Timeline} */(/** @type {unknown} */(globals.editor.addTimeline(parameters)));
4502
+ }
4503
+ return new Timeline(parameters).init();
4504
+ };
4414
4505
 
4415
4506
 
4416
4507
 
@@ -4857,6 +4948,170 @@ const createSpring = (parameters) => {
4857
4948
 
4858
4949
 
4859
4950
 
4951
+ const WAAPIAnimationsLookups = {
4952
+ _head: null,
4953
+ _tail: null,
4954
+ };
4955
+
4956
+ /**
4957
+ * @param {DOMTarget} $el
4958
+ * @param {String} [property]
4959
+ * @param {WAAPIAnimation} [parent]
4960
+ * @return {globalThis.Animation}
4961
+ */
4962
+ const removeWAAPIAnimation = ($el, property, parent) => {
4963
+ let nextLookup = WAAPIAnimationsLookups._head;
4964
+ let anim;
4965
+ while (nextLookup) {
4966
+ const next = nextLookup._next;
4967
+ const matchTarget = nextLookup.$el === $el;
4968
+ const matchProperty = !property || nextLookup.property === property;
4969
+ const matchParent = !parent || nextLookup.parent === parent;
4970
+ if (matchTarget && matchProperty && matchParent) {
4971
+ anim = nextLookup.animation;
4972
+ try { anim.commitStyles(); } catch {} anim.cancel();
4973
+ removeChild(WAAPIAnimationsLookups, nextLookup);
4974
+ const lookupParent = nextLookup.parent;
4975
+ if (lookupParent) {
4976
+ lookupParent._completed++;
4977
+ if (lookupParent.animations.length === lookupParent._completed) {
4978
+ lookupParent.completed = true;
4979
+ lookupParent.paused = true;
4980
+ if (!lookupParent.muteCallbacks) {
4981
+ lookupParent.onComplete(lookupParent);
4982
+ lookupParent._resolve(lookupParent);
4983
+ }
4984
+ }
4985
+ }
4986
+ }
4987
+ nextLookup = next;
4988
+ }
4989
+ return anim;
4990
+ };
4991
+
4992
+ /**
4993
+ * @param {WAAPIAnimation} parent
4994
+ * @param {DOMTarget} $el
4995
+ * @param {String} property
4996
+ * @param {PropertyIndexedKeyframes} keyframes
4997
+ * @param {KeyframeAnimationOptions} params
4998
+ * @retun {globalThis.Animation}
4999
+ */
5000
+ const addWAAPIAnimation = (parent, $el, property, keyframes, params) => {
5001
+ const animation = $el.animate(keyframes, params);
5002
+ const animTotalDuration = params.delay + (+params.duration * params.iterations);
5003
+ animation.playbackRate = parent._speed;
5004
+ if (parent.paused) animation.pause();
5005
+ if (parent.duration < animTotalDuration) {
5006
+ parent.duration = animTotalDuration;
5007
+ parent.controlAnimation = animation;
5008
+ }
5009
+ parent.animations.push(animation);
5010
+ removeWAAPIAnimation($el, property);
5011
+ addChild(WAAPIAnimationsLookups, { parent, animation, $el, property, _next: null, _prev: null });
5012
+ const handleRemove = () => removeWAAPIAnimation($el, property, parent);
5013
+ animation.oncancel = handleRemove;
5014
+ animation.onremove = handleRemove;
5015
+ if (!parent.persist) {
5016
+ animation.onfinish = handleRemove;
5017
+ }
5018
+ return animation;
5019
+ };
5020
+
5021
+
5022
+
5023
+
5024
+
5025
+ /**
5026
+ * @overload
5027
+ * @param {DOMTargetSelector} targetSelector
5028
+ * @param {String} propName
5029
+ * @return {String}
5030
+ *
5031
+ * @overload
5032
+ * @param {JSTargetsParam} targetSelector
5033
+ * @param {String} propName
5034
+ * @return {Number|String}
5035
+ *
5036
+ * @overload
5037
+ * @param {DOMTargetsParam} targetSelector
5038
+ * @param {String} propName
5039
+ * @param {String} unit
5040
+ * @return {String}
5041
+ *
5042
+ * @overload
5043
+ * @param {TargetsParam} targetSelector
5044
+ * @param {String} propName
5045
+ * @param {Boolean} unit
5046
+ * @return {Number}
5047
+ *
5048
+ * @param {TargetsParam} targetSelector
5049
+ * @param {String} propName
5050
+ * @param {String|Boolean} [unit]
5051
+ */
5052
+ function get(targetSelector, propName, unit) {
5053
+ const targets = registerTargets(targetSelector);
5054
+ if (!targets.length) return;
5055
+ const [ target ] = targets;
5056
+ const tweenType = getTweenType(target, propName);
5057
+ const normalizePropName = sanitizePropertyName(propName, target, tweenType);
5058
+ let originalValue = getOriginalAnimatableValue(target, normalizePropName);
5059
+ if (isUnd(unit)) {
5060
+ return originalValue;
5061
+ } else {
5062
+ decomposeRawValue(originalValue, decomposedOriginalValue);
5063
+ if (decomposedOriginalValue.t === valueTypes.NUMBER || decomposedOriginalValue.t === valueTypes.UNIT) {
5064
+ if (unit === false) {
5065
+ return decomposedOriginalValue.n;
5066
+ } else {
5067
+ const convertedValue = convertValueUnit(/** @type {DOMTarget} */(target), decomposedOriginalValue, /** @type {String} */(unit), false);
5068
+ return `${round$1(convertedValue.n, globals.precision)}${convertedValue.u}`;
5069
+ }
5070
+ }
5071
+ }
5072
+ }
5073
+
5074
+ /**
5075
+ * @param {TargetsParam} targets
5076
+ * @param {AnimationParams} parameters
5077
+ * @return {JSAnimation}
5078
+ */
5079
+ const set = (targets, parameters) => {
5080
+ if (isUnd(parameters)) return;
5081
+ parameters.duration = minValue;
5082
+ // Do not overrides currently active tweens by default
5083
+ parameters.composition = setValue(parameters.composition, compositionTypes.none);
5084
+ // Skip init() and force rendering by playing the animation
5085
+ return new JSAnimation(targets, parameters, null, 0, true).resume();
5086
+ };
5087
+
5088
+ /**
5089
+ * @param {TargetsParam} targets
5090
+ * @param {Renderable|WAAPIAnimation} [renderable]
5091
+ * @param {String} [propertyName]
5092
+ * @return {TargetsArray}
5093
+ */
5094
+ const remove = (targets, renderable, propertyName) => {
5095
+ const targetsArray = parseTargets(targets);
5096
+ for (let i = 0, l = targetsArray.length; i < l; i++) {
5097
+ removeWAAPIAnimation(
5098
+ /** @type {DOMTarget} */(targetsArray[i]),
5099
+ propertyName,
5100
+ renderable && /** @type {WAAPIAnimation} */(renderable).controlAnimation && /** @type {WAAPIAnimation} */(renderable),
5101
+ );
5102
+ }
5103
+ removeTargetsFromRenderable(
5104
+ targetsArray,
5105
+ /** @type {Renderable} */(renderable),
5106
+ propertyName
5107
+ );
5108
+ return targetsArray;
5109
+ };
5110
+
5111
+
5112
+
5113
+
5114
+
4860
5115
  /**
4861
5116
  * @param {Event} e
4862
5117
  */
@@ -6059,19 +6314,20 @@ const sync = (callback = noop) => {
6059
6314
  };
6060
6315
 
6061
6316
  /**
6062
- * @param {(...args: any[]) => Tickable | ((...args: any[]) => void)} constructor
6317
+ * @param {(...args: any[]) => Tickable | ((...args: any[]) => void) | void} constructor
6063
6318
  * @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
6064
6319
  */
6065
6320
  const keepTime = constructor => {
6066
6321
  /** @type {Tickable} */
6067
6322
  let tracked;
6068
6323
  return (...args) => {
6069
- let currentIteration, currentIterationProgress, reversed, alternate;
6324
+ let currentIteration, currentIterationProgress, reversed, alternate, startTime;
6070
6325
  if (tracked) {
6071
6326
  currentIteration = tracked.currentIteration;
6072
6327
  currentIterationProgress = tracked.iterationProgress;
6073
6328
  reversed = tracked.reversed;
6074
6329
  alternate = tracked._alternate;
6330
+ startTime = tracked._startTime;
6075
6331
  tracked.revert();
6076
6332
  }
6077
6333
  const cleanup = constructor(...args);
@@ -6079,6 +6335,7 @@ const keepTime = constructor => {
6079
6335
  if (!isUnd(currentIterationProgress)) {
6080
6336
  /** @type {Tickable} */(tracked).currentIteration = currentIteration;
6081
6337
  /** @type {Tickable} */(tracked).iterationProgress = (alternate ? !(currentIteration % 2) ? reversed : !reversed : reversed) ? 1 - currentIterationProgress : currentIterationProgress;
6338
+ /** @type {Tickable} */(tracked)._startTime = startTime;
6082
6339
  }
6083
6340
  return cleanup || noop;
6084
6341
  }
@@ -7424,12 +7681,12 @@ let transformsPropertiesRegistered = null;
7424
7681
  * @param {WAAPIKeyframeValue} value
7425
7682
  * @param {DOMTarget} $el
7426
7683
  * @param {Number} i
7427
- * @param {Number} targetsLength
7684
+ * @param {DOMTargetsArray} parsedTargets
7428
7685
  * @return {String}
7429
7686
  */
7430
- const normalizeTweenValue = (propName, value, $el, i, targetsLength) => {
7687
+ const normalizeTweenValue = (propName, value, $el, i, parsedTargets) => {
7431
7688
  // Do not try to compute strings with getFunctionValue otherwise it will convert CSS variables
7432
- let v = isStr(value) ? value : getFunctionValue(/** @type {any} */(value), $el, i, targetsLength);
7689
+ let v = isStr(value) ? value : getFunctionValue(/** @type {any} */(value), $el, i, parsedTargets, null, null);
7433
7690
  if (!isNum(v)) return v;
7434
7691
  if (commonDefaultPXProperties.includes(propName) || stringStartsWith(propName, 'translate')) return `${v}px`;
7435
7692
  if (stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew')) return `${v}deg`;
@@ -7442,18 +7699,18 @@ const normalizeTweenValue = (propName, value, $el, i, targetsLength) => {
7442
7699
  * @param {WAAPIKeyframeValue} from
7443
7700
  * @param {WAAPIKeyframeValue} to
7444
7701
  * @param {Number} i
7445
- * @param {Number} targetsLength
7702
+ * @param {DOMTargetsArray} parsedTargets
7446
7703
  * @return {WAAPITweenValue}
7447
7704
  */
7448
- const parseIndividualTweenValue = ($el, propName, from, to, i, targetsLength) => {
7705
+ const parseIndividualTweenValue = ($el, propName, from, to, i, parsedTargets) => {
7449
7706
  /** @type {WAAPITweenValue} */
7450
7707
  let tweenValue = '0';
7451
- const computedTo = !isUnd(to) ? normalizeTweenValue(propName, to, $el, i, targetsLength) : getComputedStyle($el)[propName];
7708
+ const computedTo = !isUnd(to) ? normalizeTweenValue(propName, to, $el, i, parsedTargets) : getComputedStyle($el)[propName];
7452
7709
  if (!isUnd(from)) {
7453
- const computedFrom = normalizeTweenValue(propName, from, $el, i, targetsLength);
7710
+ const computedFrom = normalizeTweenValue(propName, from, $el, i, parsedTargets);
7454
7711
  tweenValue = [computedFrom, computedTo];
7455
7712
  } else {
7456
- tweenValue = isArr(to) ? to.map((/** @type {any} */v) => normalizeTweenValue(propName, v, $el, i, targetsLength)) : computedTo;
7713
+ tweenValue = isArr(to) ? to.map((/** @type {any} */v) => normalizeTweenValue(propName, v, $el, i, parsedTargets)) : computedTo;
7457
7714
  }
7458
7715
  return tweenValue;
7459
7716
  };
@@ -7492,9 +7749,8 @@ class WAAPIAnimation {
7492
7749
  }
7493
7750
 
7494
7751
  const parsedTargets = registerTargets(targets);
7495
- const targetsLength = parsedTargets.length;
7496
7752
 
7497
- if (!targetsLength) {
7753
+ if (!parsedTargets.length) {
7498
7754
  console.warn(`No target found. Make sure the element you're trying to animate is accessible before creating your animation.`);
7499
7755
  }
7500
7756
 
@@ -7550,7 +7806,7 @@ class WAAPIAnimation {
7550
7806
 
7551
7807
  const easeToParse = setValue(params.ease, globals.defaults.ease);
7552
7808
 
7553
- const easeFunctionResult = getFunctionValue(easeToParse, $el, i, targetsLength);
7809
+ const easeFunctionResult = getFunctionValue(easeToParse, $el, i, parsedTargets, null, null);
7554
7810
  const keyEasing = isFnc(easeFunctionResult) || isStr(easeFunctionResult) ? easeFunctionResult : easeToParse;
7555
7811
 
7556
7812
  const spring = /** @type {Spring} */(easeToParse).ease && easeToParse;
@@ -7558,9 +7814,9 @@ class WAAPIAnimation {
7558
7814
  const easing = parseWAAPIEasing(keyEasing);
7559
7815
 
7560
7816
  /** @type {Number} */
7561
- const duration = (spring ? /** @type {Spring} */(spring).settlingDuration : getFunctionValue(setValue(params.duration, globals.defaults.duration), $el, i, targetsLength)) * timeScale;
7817
+ const duration = (spring ? /** @type {Spring} */(spring).settlingDuration : getFunctionValue(setValue(params.duration, globals.defaults.duration), $el, i, parsedTargets, null, null)) * timeScale;
7562
7818
  /** @type {Number} */
7563
- const delay = getFunctionValue(setValue(params.delay, globals.defaults.delay), $el, i, targetsLength) * timeScale;
7819
+ const delay = getFunctionValue(setValue(params.delay, globals.defaults.delay), $el, i, parsedTargets, null, null) * timeScale;
7564
7820
  /** @type {CompositeOperation} */
7565
7821
  const composite = /** @type {CompositeOperation} */(setValue(params.composition, 'replace'));
7566
7822
 
@@ -7586,19 +7842,19 @@ class WAAPIAnimation {
7586
7842
  const to = /** @type {WAAPITweenOptions} */(tweenOptions).to;
7587
7843
  const from = /** @type {WAAPITweenOptions} */(tweenOptions).from;
7588
7844
  /** @type {Number} */
7589
- tweenParams.duration = (tweenOptionsSpring ? /** @type {Spring} */(tweenOptionsSpring).settlingDuration : getFunctionValue(setValue(tweenOptions.duration, duration), $el, i, targetsLength)) * timeScale;
7845
+ tweenParams.duration = (tweenOptionsSpring ? /** @type {Spring} */(tweenOptionsSpring).settlingDuration : getFunctionValue(setValue(tweenOptions.duration, duration), $el, i, parsedTargets, null, null)) * timeScale;
7590
7846
  /** @type {Number} */
7591
- tweenParams.delay = getFunctionValue(setValue(tweenOptions.delay, delay), $el, i, targetsLength) * timeScale;
7847
+ tweenParams.delay = getFunctionValue(setValue(tweenOptions.delay, delay), $el, i, parsedTargets, null, null) * timeScale;
7592
7848
  /** @type {CompositeOperation} */
7593
7849
  tweenParams.composite = /** @type {CompositeOperation} */(setValue(tweenOptions.composition, composite));
7594
7850
  /** @type {String} */
7595
7851
  tweenParams.easing = parseWAAPIEasing(tweenOptionsEase);
7596
- parsedPropertyValue = parseIndividualTweenValue($el, name, from, to, i, targetsLength);
7852
+ parsedPropertyValue = parseIndividualTweenValue($el, name, from, to, i, parsedTargets);
7597
7853
  if (individualTransformProperty) {
7598
7854
  keyframes[`--${individualTransformProperty}`] = parsedPropertyValue;
7599
7855
  cachedTransforms[individualTransformProperty] = parsedPropertyValue;
7600
7856
  } else {
7601
- keyframes[name] = parseIndividualTweenValue($el, name, from, to, i, targetsLength);
7857
+ keyframes[name] = parseIndividualTweenValue($el, name, from, to, i, parsedTargets);
7602
7858
  }
7603
7859
  addWAAPIAnimation(this, $el, name, keyframes, tweenParams);
7604
7860
  if (!isUnd(from)) {
@@ -7611,8 +7867,8 @@ class WAAPIAnimation {
7611
7867
  }
7612
7868
  } else {
7613
7869
  parsedPropertyValue = isArr(propertyValue) ?
7614
- propertyValue.map((/** @type {any} */v) => normalizeTweenValue(name, v, $el, i, targetsLength)) :
7615
- normalizeTweenValue(name, /** @type {any} */(propertyValue), $el, i, targetsLength);
7870
+ propertyValue.map((/** @type {any} */v) => normalizeTweenValue(name, v, $el, i, parsedTargets)) :
7871
+ normalizeTweenValue(name, /** @type {any} */(propertyValue), $el, i, parsedTargets);
7616
7872
  if (individualTransformProperty) {
7617
7873
  keyframes[`--${individualTransformProperty}`] = parsedPropertyValue;
7618
7874
  cachedTransforms[individualTransformProperty] = parsedPropertyValue;
@@ -7845,6 +8101,7 @@ const waapi = {
7845
8101
 
7846
8102
  /**
7847
8103
  * @typedef {Object} LayoutSpecificAnimationParams
8104
+ * @property {Number|String} [id]
7848
8105
  * @property {Number|FunctionValue} [delay]
7849
8106
  * @property {Number|FunctionValue} [duration]
7850
8107
  * @property {EasingParam|FunctionValue} [ease]
@@ -7887,7 +8144,7 @@ const waapi = {
7887
8144
  * @property {String} id
7888
8145
  * @property {DOMTarget} $el
7889
8146
  * @property {Number} index
7890
- * @property {Number} total
8147
+ * @property {Array<DOMTarget>} targets
7891
8148
  * @property {Number} delay
7892
8149
  * @property {Number} duration
7893
8150
  * @property {EasingParam} ease
@@ -8031,7 +8288,7 @@ const createNode = ($el, parentNode, state, recycledNode) => {
8031
8288
  node.$measure = $el;
8032
8289
  node.id = dataId;
8033
8290
  node.index = 0;
8034
- node.total = 1;
8291
+ node.targets = null;
8035
8292
  node.delay = 0;
8036
8293
  node.duration = 0;
8037
8294
  node.ease = null;
@@ -8217,12 +8474,12 @@ const updateNodeProperties = (node, props) => {
8217
8474
  * @param {LayoutAnimationTimingsParams} params
8218
8475
  */
8219
8476
  const updateNodeTimingParams = (node, params) => {
8220
- const easeFunctionResult = getFunctionValue(params.ease, node.$el, node.index, node.total);
8477
+ const easeFunctionResult = getFunctionValue(params.ease, node.$el, node.index, node.targets, null, null);
8221
8478
  const keyEasing = isFnc(easeFunctionResult) ? easeFunctionResult : params.ease;
8222
8479
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
8223
8480
  node.ease = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
8224
- node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.total);
8225
- node.delay = getFunctionValue(params.delay, node.$el, node.index, node.total);
8481
+ node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.targets, null, null);
8482
+ node.delay = getFunctionValue(params.delay, node.$el, node.index, node.targets, null, null);
8226
8483
  };
8227
8484
 
8228
8485
  /**
@@ -8595,10 +8852,12 @@ class LayoutSnapshot {
8595
8852
 
8596
8853
  const inRootNodeIds = new Set();
8597
8854
  // Update index and total for inital timing calculation
8598
- let index = 0, total = this.nodes.size;
8855
+ let index = 0;
8856
+ const allNodeTargets = [];
8857
+ this.nodes.forEach((node) => { allNodeTargets.push(node.$el); });
8599
8858
  this.nodes.forEach((node, id) => {
8600
8859
  node.index = index++;
8601
- node.total = total;
8860
+ node.targets = allNodeTargets;
8602
8861
  // Track ids of nodes that belong to the current root to filter detached matches
8603
8862
  if (node && node.measuredIsInsideRoot) {
8604
8863
  inRootNodeIds.add(id);
@@ -8710,8 +8969,8 @@ class AutoLayout {
8710
8969
  this.params = params;
8711
8970
  /** @type {DOMTarget} */
8712
8971
  this.root = /** @type {DOMTarget} */(registerTargets(root)[0]);
8713
- /** @type {Number} */
8714
- this.id = layoutId++;
8972
+ /** @type {Number|String} */
8973
+ this.id = params.id || layoutId++;
8715
8974
  /** @type {LayoutChildrenParam} */
8716
8975
  this.children = params.children || '*';
8717
8976
  /** @type {Boolean} */
@@ -8834,7 +9093,9 @@ class AutoLayout {
8834
9093
  duration: setValue(params.duration, this.params.duration),
8835
9094
  };
8836
9095
  /** @type {TimelineParams} */
8837
- const tlParams = {};
9096
+ const tlParams = {
9097
+ id: this.id
9098
+ };
8838
9099
  const onComplete = setValue(params.onComplete, this.params.onComplete);
8839
9100
  const onPause = setValue(params.onPause, this.params.onPause);
8840
9101
  for (let name in defaults) {
@@ -8848,7 +9109,8 @@ class AutoLayout {
8848
9109
  }
8849
9110
  tlParams.onComplete = () => {
8850
9111
  const ap = /** @type {ScrollObserver} */(params.autoplay);
8851
- const isScrollControled = ap && ap.linked;
9112
+ const ed = globals.editor;
9113
+ const isScrollControled = (ap && ap.linked) || (ed && ed.showPanel);
8852
9114
  if (isScrollControled) {
8853
9115
  if (onComplete) onComplete(this.timeline);
8854
9116
  return;
@@ -9047,41 +9309,39 @@ class AutoLayout {
9047
9309
  animatedParent = animatedParent.parentNode;
9048
9310
  }
9049
9311
 
9050
- const animatingTotal = animating.length;
9051
-
9052
9312
  // Root is always animated first in sync with the first child (animating.length is the total of children)
9053
9313
  if (node === rootNode) {
9054
9314
  node.index = 0;
9055
- node.total = animatingTotal;
9315
+ node.targets = animating;
9056
9316
  updateNodeTimingParams(node, animationTimings);
9057
9317
  } else if (node.isEntering) {
9058
9318
  node.index = animatedParent ? animatedParent.index : enteringIndex;
9059
- node.total = animatedParent ? animatingTotal : entering.length;
9319
+ node.targets = animatedParent ? animating : entering;
9060
9320
  updateNodeTimingParams(node, enterFromTimings);
9061
9321
  enteringIndex++;
9062
9322
  } else if (node.isLeaving) {
9063
9323
  node.index = animatedParent ? animatedParent.index : leavingIndex;
9064
- node.total = animatedParent ? animatingTotal : leaving.length;
9324
+ node.targets = animatedParent ? animating : leaving;
9065
9325
  leavingIndex++;
9066
9326
  updateNodeTimingParams(node, leaveToTimings);
9067
9327
  } else if (node.isTarget) {
9068
9328
  node.index = animatingIndex++;
9069
- node.total = animatingTotal;
9329
+ node.targets = animating;
9070
9330
  updateNodeTimingParams(node, animationTimings);
9071
9331
  } else {
9072
9332
  node.index = animatedParent ? animatedParent.index : 0;
9073
- node.total = animatingTotal;
9333
+ node.targets = animating;
9074
9334
  updateNodeTimingParams(node, swapAtTimings);
9075
9335
  }
9076
9336
 
9077
9337
  // Make sure the old state node has its inex and total values up to date for valid "from" function values calculation
9078
9338
  oldStateNode.index = node.index;
9079
- oldStateNode.total = node.total;
9339
+ oldStateNode.targets = node.targets;
9080
9340
 
9081
9341
  // Computes all values up front so we can check for changes and we don't have to re-compute them inside the animation props
9082
9342
  for (let prop in nodeProperties) {
9083
- nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.total);
9084
- oldStateNodeProperties[prop] = getFunctionValue(oldStateNodeProperties[prop], $el, oldStateNode.index, oldStateNode.total);
9343
+ nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.targets, null, null);
9344
+ oldStateNodeProperties[prop] = getFunctionValue(oldStateNodeProperties[prop], $el, oldStateNode.index, oldStateNode.targets, null, null);
9085
9345
  }
9086
9346
 
9087
9347
  // Use a 1px tolerance to detect dimensions changes to prevent width / height animations on barelly visible elements
@@ -9307,8 +9567,8 @@ class AutoLayout {
9307
9567
  }
9308
9568
  $el.style.transform = oldState.getComputedValue($el, 'transform');
9309
9569
  if (animatedSwap.includes($el)) {
9310
- node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.total);
9311
- node.duration = getFunctionValue(swapAtParams.duration, $el, node.index, node.total);
9570
+ node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.targets, null, null);
9571
+ node.duration = getFunctionValue(swapAtParams.duration, $el, node.index, node.targets, null, null);
9312
9572
  }
9313
9573
  }
9314
9574
  this.transformAnimation = waapi.animate(transformed, {
@@ -9323,7 +9583,7 @@ class AutoLayout {
9323
9583
  if (!animatedSwap.includes($el)) return newValue;
9324
9584
  const oldValue = oldState.getComputedValue($el, 'transform');
9325
9585
  const node = newState.getNode($el);
9326
- return [oldValue, getFunctionValue(swapAtProps.transform, $el, node.index, node.total), newValue]
9586
+ return [oldValue, getFunctionValue(swapAtProps.transform, $el, node.index, node.targets, null, null), newValue]
9327
9587
  },
9328
9588
  autoplay: false,
9329
9589
  // persist: true,
@@ -9380,10 +9640,13 @@ const chain = fn => {
9380
9640
  const result = fn(...args);
9381
9641
  return new Proxy(noop, {
9382
9642
  apply: (_, __, [v]) => result(v),
9383
- get: (_, prop) => chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9384
- const nextResult = chainables[prop](...nextArgs);
9385
- return (/**@type {Number|String} */v) => nextResult(result(v));
9386
- })
9643
+ get: (_, prop) => {
9644
+ if (!chainables[prop]) return undefined;
9645
+ return chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9646
+ const nextResult = chainables[prop](...nextArgs);
9647
+ return (/**@type {Number|String} */v) => nextResult(result(v));
9648
+ })
9649
+ }
9387
9650
  });
9388
9651
  }
9389
9652
  };
@@ -9594,24 +9857,28 @@ const shuffle = items => {
9594
9857
  * @param {StaggerParams} [params]
9595
9858
  * @return {StaggerFunction<Number>}
9596
9859
  */
9860
+
9597
9861
  /**
9598
9862
  * @overload
9599
9863
  * @param {String} val
9600
9864
  * @param {StaggerParams} [params]
9601
9865
  * @return {StaggerFunction<String>}
9602
9866
  */
9867
+
9603
9868
  /**
9604
9869
  * @overload
9605
9870
  * @param {[Number, Number]} val
9606
9871
  * @param {StaggerParams} [params]
9607
9872
  * @return {StaggerFunction<Number>}
9608
9873
  */
9874
+
9609
9875
  /**
9610
9876
  * @overload
9611
9877
  * @param {[String, String]} val
9612
9878
  * @param {StaggerParams} [params]
9613
9879
  * @return {StaggerFunction<String>}
9614
9880
  */
9881
+
9615
9882
  /**
9616
9883
  * @param {Number|String|[Number, Number]|[String, String]} val The staggered value or range
9617
9884
  * @param {StaggerParams} [params] The stagger parameters
@@ -9620,6 +9887,7 @@ const shuffle = items => {
9620
9887
  const stagger = (val, params = {}) => {
9621
9888
  let values = [];
9622
9889
  let maxValue = 0;
9890
+ let cachedOffset;
9623
9891
  const from = params.from;
9624
9892
  const reversed = params.reversed;
9625
9893
  const ease = params.ease;
@@ -9627,12 +9895,14 @@ const stagger = (val, params = {}) => {
9627
9895
  const hasSpring = hasEasing && !isUnd(/** @type {Spring} */(ease).ease);
9628
9896
  const staggerEase = hasSpring ? /** @type {Spring} */(ease).ease : hasEasing ? parseEase(ease) : null;
9629
9897
  const grid = params.grid;
9898
+ const autoGrid = grid === true;
9630
9899
  const axis = params.axis;
9631
9900
  const customTotal = params.total;
9632
9901
  const fromFirst = isUnd(from) || from === 0 || from === 'first';
9633
9902
  const fromCenter = from === 'center';
9634
9903
  const fromLast = from === 'last';
9635
9904
  const fromRandom = from === 'random';
9905
+ const fromArr = isArr(from);
9636
9906
  const isRange = isArr(val);
9637
9907
  const useProp = params.use;
9638
9908
  const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
@@ -9640,40 +9910,129 @@ const stagger = (val, params = {}) => {
9640
9910
  const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
9641
9911
  const start = params.start || 0 + (isRange ? val1 : 0);
9642
9912
  let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
9643
- return (target, i, t, tl) => {
9913
+ return (target, i, t, _, tl) => {
9644
9914
  const [ registeredTarget ] = registerTargets(target);
9645
- const total = isUnd(customTotal) ? t : customTotal;
9915
+ const total = isUnd(customTotal) ? t.length : customTotal;
9646
9916
  const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
9647
9917
  const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
9648
9918
  if (fromCenter) fromIndex = (total - 1) / 2;
9649
9919
  if (fromLast) fromIndex = total - 1;
9650
9920
  if (!values.length) {
9651
- for (let index = 0; index < total; index++) {
9652
- if (!grid) {
9653
- values.push(abs(fromIndex - index));
9921
+ if (autoGrid) {
9922
+ let hasPositions = true;
9923
+ let minPosX = Infinity;
9924
+ let minPosY = Infinity;
9925
+ let maxPosX = -Infinity;
9926
+ let maxPosY = -Infinity;
9927
+ const pxArr = [];
9928
+ const pyArr = [];
9929
+ for (let index = 0; index < total; index++) {
9930
+ const el = t[index];
9931
+ let px = 0;
9932
+ let py = 0;
9933
+ let found = false;
9934
+ if (el && isFnc(el.getBoundingClientRect)) {
9935
+ const rect = el.getBoundingClientRect();
9936
+ px = rect.left + rect.width / 2;
9937
+ py = rect.top + rect.height / 2;
9938
+ found = true;
9939
+ } else {
9940
+ const obj = /** @type {JSTarget} */(el);
9941
+ if (obj && isNum(obj.x) && isNum(obj.y)) {
9942
+ px = obj.x;
9943
+ py = obj.y;
9944
+ found = true;
9945
+ }
9946
+ }
9947
+ if (!found) {
9948
+ hasPositions = false;
9949
+ break;
9950
+ }
9951
+ pxArr.push(px);
9952
+ pyArr.push(py);
9953
+ if (px < minPosX) minPosX = px;
9954
+ if (py < minPosY) minPosY = py;
9955
+ if (px > maxPosX) maxPosX = px;
9956
+ if (py > maxPosY) maxPosY = py;
9957
+ }
9958
+ if (hasPositions) {
9959
+ let fX = pxArr[0];
9960
+ let fY = pyArr[0];
9961
+ if (fromArr) {
9962
+ fX = minPosX + from[0] * (maxPosX - minPosX);
9963
+ fY = minPosY + from[1] * (maxPosY - minPosY);
9964
+ } else if (fromCenter) {
9965
+ fX = (minPosX + maxPosX) / 2;
9966
+ fY = (minPosY + maxPosY) / 2;
9967
+ } else if (fromLast) {
9968
+ fX = pxArr[total - 1];
9969
+ fY = pyArr[total - 1];
9970
+ } else if (isNum(from)) {
9971
+ fX = pxArr[from];
9972
+ fY = pyArr[from];
9973
+ }
9974
+ for (let index = 0; index < total; index++) {
9975
+ const distanceX = fX - pxArr[index];
9976
+ const distanceY = fY - pyArr[index];
9977
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9978
+ if (axis === 'x') value = -distanceX;
9979
+ if (axis === 'y') value = -distanceY;
9980
+ values.push(value);
9981
+ }
9982
+ let minDist = Infinity;
9983
+ for (let index = 0, l = values.length; index < l; index++) {
9984
+ const absVal = abs(values[index]);
9985
+ if (absVal > 0 && absVal < minDist) minDist = absVal;
9986
+ }
9987
+ if (minDist > 0 && minDist < Infinity) {
9988
+ for (let index = 0, l = values.length; index < l; index++) {
9989
+ values[index] = values[index] / minDist;
9990
+ }
9991
+ }
9654
9992
  } else {
9655
- const fromX = !fromCenter ? fromIndex % grid[0] : (grid[0] - 1) / 2;
9656
- const fromY = !fromCenter ? floor(fromIndex / grid[0]) : (grid[1] - 1) / 2;
9657
- const toX = index % grid[0];
9658
- const toY = floor(index / grid[0]);
9659
- const distanceX = fromX - toX;
9660
- const distanceY = fromY - toY;
9661
- let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9662
- if (axis === 'x') value = -distanceX;
9663
- if (axis === 'y') value = -distanceY;
9664
- values.push(value);
9993
+ for (let index = 0; index < total; index++) {
9994
+ values.push(abs(fromIndex - index));
9995
+ }
9996
+ }
9997
+ } else {
9998
+ for (let index = 0; index < total; index++) {
9999
+ if (!grid) {
10000
+ values.push(abs(fromIndex - index));
10001
+ } else {
10002
+ let fromX, fromY;
10003
+ if (fromArr) {
10004
+ fromX = from[0] * (grid[0] - 1);
10005
+ fromY = from[1] * (grid[1] - 1);
10006
+ } else if (fromCenter) {
10007
+ fromX = (grid[0] - 1) / 2;
10008
+ fromY = (grid[1] - 1) / 2;
10009
+ } else {
10010
+ fromX = fromIndex % grid[0];
10011
+ fromY = floor(fromIndex / grid[0]);
10012
+ }
10013
+ const toX = index % grid[0];
10014
+ const toY = floor(index / grid[0]);
10015
+ const distanceX = fromX - toX;
10016
+ const distanceY = fromY - toY;
10017
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY);
10018
+ if (axis === 'x') value = -distanceX;
10019
+ if (axis === 'y') value = -distanceY;
10020
+ values.push(value);
10021
+ }
9665
10022
  }
9666
- maxValue = max(...values);
9667
10023
  }
10024
+ maxValue = max(...values);
9668
10025
  if (staggerEase) values = values.map(val => staggerEase(val / maxValue) * maxValue);
9669
10026
  if (reversed) values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
9670
10027
  if (fromRandom) values = shuffle(values);
9671
10028
  }
9672
10029
  const spacing = isRange ? (val2 - val1) / maxValue : val1;
9673
- const offset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
10030
+ if (isUnd(cachedOffset)) {
10031
+ cachedOffset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
10032
+ }
9674
10033
  /** @type {String|Number} */
9675
- let output = offset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
9676
- if (params.modifier) output = params.modifier(output);
10034
+ let output = cachedOffset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
10035
+ if (params.modifier) output = params.modifier(/** @type {Number} */(output));
9677
10036
  if (unitMatch) output = `${output}${unitMatch[2]}`;
9678
10037
  return output;
9679
10038
  }
@@ -9682,11 +10041,13 @@ const stagger = (val, params = {}) => {
9682
10041
  var index$2 = /*#__PURE__*/Object.freeze({
9683
10042
  __proto__: null,
9684
10043
  $: registerTargets,
10044
+ addChild: addChild,
9685
10045
  clamp: clamp,
9686
10046
  cleanInlineStyles: cleanInlineStyles,
9687
10047
  createSeededRandom: createSeededRandom,
9688
10048
  damp: damp,
9689
10049
  degToRad: degToRad,
10050
+ forEachChildren: forEachChildren,
9690
10051
  get: get,
9691
10052
  keepTime: keepTime,
9692
10053
  lerp: lerp,
@@ -9697,6 +10058,7 @@ var index$2 = /*#__PURE__*/Object.freeze({
9697
10058
  random: random,
9698
10059
  randomPick: randomPick,
9699
10060
  remove: remove,
10061
+ removeChild: removeChild,
9700
10062
  round: round,
9701
10063
  roundPad: roundPad,
9702
10064
  set: set,
@@ -9896,7 +10258,7 @@ const createDrawable = (selector, start = 0, end = 0) => {
9896
10258
  * @param {Number} [precision]
9897
10259
  * @return {FunctionValue}
9898
10260
  */
9899
- const morphTo = (path2, precision = .33) => ($path1) => {
10261
+ const morphTo = (path2, precision = .33) => ($path1, index, total, prevTween) => {
9900
10262
  const tagName1 = ($path1.tagName || '').toLowerCase();
9901
10263
  if (!tagName1.match(/^(path|polygon|polyline)$/)) {
9902
10264
  throw new Error(`Can't morph a <${$path1.tagName}> SVG element. Use <path>, <polygon> or <polyline>.`);
@@ -9911,7 +10273,7 @@ const morphTo = (path2, precision = .33) => ($path1) => {
9911
10273
  }
9912
10274
  const isPath = $path1.tagName === 'path';
9913
10275
  const separator = isPath ? ' ' : ',';
9914
- const previousPoints = $path1[morphPointsSymbol];
10276
+ const previousPoints = prevTween ? prevTween._value : null;
9915
10277
  if (previousPoints) $path1.setAttribute(isPath ? 'd' : 'points', previousPoints);
9916
10278
 
9917
10279
  let v1 = '', v2 = '';
@@ -9933,8 +10295,6 @@ const morphTo = (path2, precision = .33) => ($path1) => {
9933
10295
  }
9934
10296
  }
9935
10297
 
9936
- $path1[morphPointsSymbol] = v2;
9937
-
9938
10298
  return [v1, v2];
9939
10299
  };
9940
10300
 
@@ -10120,7 +10480,7 @@ const processHTMLTemplate = (htmlTemplate, store, node, $parentFragment, type, d
10120
10480
  */
10121
10481
  class TextSplitter {
10122
10482
  /**
10123
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10483
+ * @param {Element|NodeList|String|Array<Element>} target
10124
10484
  * @param {TextSplitterParams} [parameters]
10125
10485
  */
10126
10486
  constructor(target, parameters = {}) {
@@ -10192,11 +10552,11 @@ class TextSplitter {
10192
10552
  }
10193
10553
 
10194
10554
  /**
10195
- * @param {(...args: any[]) => Tickable | (() => void)} effect
10555
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
10196
10556
  * @return this
10197
10557
  */
10198
10558
  addEffect(effect) {
10199
- if (!isFnc(effect)) return console.warn('Effect must return a function.');
10559
+ if (!isFnc(effect)) { console.warn('Effect must return a function.'); return this; }
10200
10560
  const refreshableEffect = keepTime(effect);
10201
10561
  this.effects.push(refreshableEffect);
10202
10562
  if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
@@ -10402,7 +10762,7 @@ class TextSplitter {
10402
10762
  }
10403
10763
 
10404
10764
  /**
10405
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10765
+ * @param {Element|NodeList|String|Array<Element>} target
10406
10766
  * @param {TextSplitterParams} [parameters]
10407
10767
  * @return {TextSplitter}
10408
10768
  */
@@ -10420,11 +10780,263 @@ const split = (target, parameters) => {
10420
10780
  return new TextSplitter(target, parameters);
10421
10781
  };
10422
10782
 
10783
+
10784
+
10785
+ /**
10786
+ * '-' 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-')
10787
+ * @param {String} str
10788
+ * @return {String}
10789
+ */
10790
+ const expandCharRanges = (str) => {
10791
+ let result = '';
10792
+ for (let i = 0, l = str.length; i < l; i++) {
10793
+ if (i + 2 < l && str[i + 1] === '-' && str.charCodeAt(i) < str.charCodeAt(i + 2)) {
10794
+ const start = str.charCodeAt(i);
10795
+ const end = str.charCodeAt(i + 2);
10796
+ for (let c = start; c <= end; c++) result += String.fromCharCode(c);
10797
+ i += 2;
10798
+ } else {
10799
+ result += str[i];
10800
+ }
10801
+ }
10802
+ return result;
10803
+ };
10804
+
10805
+ const charSets = {
10806
+ lowercase: 'a-z',
10807
+ uppercase: 'A-Z',
10808
+ numbers: '0-9',
10809
+ symbols: '!%#_|*+=',
10810
+ braille: '⠀-⣿',
10811
+ blocks: '▀-▟',
10812
+ shades: '░-▓',
10813
+ };
10814
+
10815
+ const originalTexts = new WeakMap();
10816
+
10817
+ /**
10818
+ * Returns a function-based tween value that scrambles the target's text content,
10819
+ * progressively revealing the original text.
10820
+ *
10821
+ * @param {ScrambleTextParams} [params]
10822
+ * @return {FunctionValue}
10823
+ */
10824
+ const scrambleText = (params = {}) => {
10825
+ if (!params) params = {};
10826
+ const charsParam = params.chars;
10827
+ const easeFn = parseEase(params.ease || 'linear');
10828
+ const text = params.text;
10829
+ const fromParam = params.from;
10830
+ const reversed = params.reversed || false;
10831
+ const perturbation = params.perturbation || 0;
10832
+ const cursorParam = params.cursor;
10833
+ const cursorChars = cursorParam === true ? '_'
10834
+ : typeof cursorParam === 'number' ? String.fromCharCode(cursorParam)
10835
+ : typeof cursorParam === 'string' ? cursorParam
10836
+ : '';
10837
+ const cursorLen = cursorChars.length;
10838
+ const seed = params.seed || 0;
10839
+ const override = params.override !== undefined ? params.override : true;
10840
+ const revealRate = params.revealRate || 60;
10841
+ const interval = 1000 * globals.timeScale / revealRate;
10842
+ const settleDuration = params.settleDuration || 300 * globals.timeScale;
10843
+ const settleRate = params.settleRate || 30;
10844
+ const durationParam = params.duration;
10845
+ const revealDelayParam = params.revealDelay;
10846
+ const delayParam = params.delay;
10847
+ const onChange = params.onChange || noop;
10848
+
10849
+ return (target, index, targets, prevTween) => {
10850
+ const rawChars = typeof charsParam === 'function' ? charsParam(target, index, targets) : (charsParam || 'a-zA-Z0-9!%#_');
10851
+ const characters = expandCharRanges(charSets[rawChars] || rawChars);
10852
+ const totalChars = characters.length - 1;
10853
+ const duration = typeof durationParam === 'function' ? durationParam(target, index, targets) : durationParam;
10854
+ const revealDelay = typeof revealDelayParam === 'function' ? revealDelayParam(target, index, targets) : (revealDelayParam || 0);
10855
+ const delay = typeof delayParam === 'function' ? delayParam(target, index, targets) : (delayParam || 0);
10856
+ const rng = seed ? createSeededRandom(seed) : createSeededRandom();
10857
+ if (!originalTexts.has(target)) originalTexts.set(target, target.textContent);
10858
+ const startingText = prevTween ? prevTween._value : target.textContent;
10859
+ const targetText = text !== undefined
10860
+ ? (typeof text === 'function' ? text(target, index, targets) : text)
10861
+ : prevTween ? prevTween._value
10862
+ : originalTexts.get(target);
10863
+ const settledText = targetText === ' ' || targetText === '&nbsp;' ? ' ' : targetText;
10864
+ const startLength = startingText === ' ' ? 0 : startingText.length;
10865
+ const endLength = settledText.length;
10866
+ const overrideChars = override === true ? characters
10867
+ : typeof override === 'string' && override.length > 0 ? expandCharRanges(charSets[/** @type {String} */(override)] || /** @type {String} */(override))
10868
+ : null;
10869
+ const totalOverrideChars = overrideChars ? overrideChars.length - 1 : 0;
10870
+ // Space override uses &nbsp; so the browser doesn't collapse consecutive spaces in innerHTML
10871
+ const overrideChar = override === ' ' ? ' ' : null;
10872
+ // When starting from blank, only animate the target text length to avoid padding beyond it
10873
+ const animLength = override === '' ? endLength : Math.max(startLength, endLength);
10874
+ // Compute total duration from interval spacing and settle time, or use the explicit duration
10875
+ const animDuration = duration > 0 ? duration : (animLength - 1) * interval + settleDuration;
10876
+ const computedDuration = round$1((animDuration + revealDelay) / globals.timeScale, 0) * globals.timeScale;
10877
+ const revealDelayRatio = revealDelay > 0 ? round$1(revealDelay / computedDuration, 12) : 0;
10878
+ // Auto-resolve reveal direction: shrinking text reveals from right, growing from left
10879
+ const resolvedFrom = fromParam === undefined || fromParam === 'auto' ? (endLength < startLength ? 'right' : 'left') : fromParam;
10880
+ const charOrder = new Int32Array(animLength);
10881
+ if (resolvedFrom === 'random') {
10882
+ for (let i = 0; i < animLength; i++) charOrder[i] = i;
10883
+ for (let i = animLength - 1; i > 0; i--) {
10884
+ const j = rng(0, i);
10885
+ const t = charOrder[i]; charOrder[i] = charOrder[j]; charOrder[j] = t;
10886
+ }
10887
+ } else {
10888
+ const ref = resolvedFrom === 'right' ? (override === '' || !startLength ? animLength : startLength) - 1
10889
+ : resolvedFrom === 'center' ? ((override === '' || !startLength ? animLength : startLength) - 1) / 2
10890
+ : typeof resolvedFrom === 'number' ? resolvedFrom
10891
+ : 0;
10892
+ const abs = Math.abs;
10893
+ const indices = new Array(animLength);
10894
+ for (let i = 0; i < animLength; i++) indices[i] = i;
10895
+ indices.sort((a, b) => abs(a - ref) - abs(b - ref));
10896
+ for (let i = 0; i < animLength; i++) charOrder[indices[i]] = i;
10897
+ }
10898
+ if (reversed) {
10899
+ const last = animLength - 1;
10900
+ for (let i = 0; i < animLength; i++) charOrder[i] = last - charOrder[i];
10901
+ }
10902
+ // settleRatio is the fraction of the animation each character spends in the active scrambling zone
10903
+ const settleRatio = round$1(settleDuration / animDuration, 12);
10904
+ // settleSpacing is the time gap between consecutive characters entering the active zone
10905
+ const settleSpacing = round$1((1 - settleRatio) / animLength, 12);
10906
+ const cursorZone = cursorLen * settleSpacing;
10907
+ // stepRatio controls how often scramble characters refresh (based on settleRate)
10908
+ const stepRatio = round$1(1000 * globals.timeScale / (settleRate * computedDuration), 12);
10909
+ // Pre-compute per-character start and settle times
10910
+ const charStarts = new Float32Array(animLength);
10911
+ const charEnds = new Float32Array(animLength);
10912
+ const scale = perturbation > 0 ? perturbation * settleRatio : 0;
10913
+ for (let c = 0; c < animLength; c++) {
10914
+ const so = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
10915
+ const eo = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
10916
+ charStarts[c] = charOrder[c] * settleSpacing + so;
10917
+ charEnds[c] = Math.ceil((charStarts[c] + settleRatio + eo) / stepRatio) * stepRatio;
10918
+ }
10919
+ // When text shrinks with non-sequential from modes, delay target settle times past all extras
10920
+ if (endLength < animLength && resolvedFrom !== 'left' && resolvedFrom !== 'right' && resolvedFrom !== 'random') {
10921
+ let maxExtraEnd = 0;
10922
+ for (let c = endLength; c < animLength; c++) {
10923
+ if (charEnds[c] > maxExtraEnd) maxExtraEnd = charEnds[c];
10924
+ }
10925
+ const targets = new Array(endLength);
10926
+ for (let c = 0; c < endLength; c++) targets[c] = c;
10927
+ targets.sort((a, b) => charOrder[a] - charOrder[b]);
10928
+ const targetSpacing = (1 - maxExtraEnd) / endLength;
10929
+ for (let i = 0; i < endLength; i++) {
10930
+ const revealTime = maxExtraEnd + i * targetSpacing;
10931
+ if (revealTime > charEnds[targets[i]]) {
10932
+ charEnds[targets[i]] = revealTime;
10933
+ }
10934
+ }
10935
+ }
10936
+ // charCache holds the current scramble character for each position, refreshed at settleRate
10937
+ const charCache = new Array(animLength);
10938
+ for (let c = 0; c < animLength; c++) {
10939
+ charCache[c] = characters[rng(0, totalChars)];
10940
+ }
10941
+ // overrideCache holds scramble characters for the starting text (override: true or custom string)
10942
+ const overrideCache = overrideChars ? (overrideChars === characters ? charCache : new Array(animLength)) : null;
10943
+ if (overrideCache && overrideCache !== charCache) {
10944
+ for (let c = 0; c < animLength; c++) {
10945
+ overrideCache[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, overrideChars.length - 1)];
10946
+ }
10947
+ }
10948
+ // Build the initial display text based on override mode
10949
+ let fillStartText = startingText;
10950
+ if (!prevTween) {
10951
+ if (override === '') {
10952
+ fillStartText = '';
10953
+ } else if (overrideChars) {
10954
+ fillStartText = '';
10955
+ for (let c = 0; c < startLength; c++) {
10956
+ fillStartText += startingText[c] === ' ' ? ' ' : /** @type {Array<String>} */(overrideCache)[c];
10957
+ }
10958
+ }
10959
+ }
10960
+
10961
+ let lastValue = -1;
10962
+ let lastStep = -1;
10963
+ let scrambled = '';
10964
+ const hasOverride = override !== '';
10965
+ const hasOverrideChars = !!overrideChars;
10966
+ const hasCursor = cursorLen > 0;
10967
+
10968
+ return {
10969
+ from: 0,
10970
+ to: 1,
10971
+ duration: computedDuration,
10972
+ delay: delay,
10973
+ ease: 'linear',
10974
+ modifier: (v) => {
10975
+ if (v === lastValue) return scrambled;
10976
+ lastValue = v;
10977
+ if (delay > 0 && v <= 0) { scrambled = startingText; return startingText; }
10978
+ if (v <= 0) { scrambled = fillStartText; return fillStartText; }
10979
+ if (v >= 1) { scrambled = settledText; return settledText; }
10980
+ scrambled = '';
10981
+ // Only refresh scramble characters when we cross a settleRate step boundary
10982
+ const currentStep = (v / stepRatio) | 0;
10983
+ const refreshChars = currentStep !== lastStep;
10984
+ if (refreshChars) lastStep = currentStep;
10985
+ // Subtract delay ratio to get the effective animation progress
10986
+ const linear = revealDelayRatio > 0 ? (v - revealDelayRatio) / (1 - revealDelayRatio) : v;
10987
+ const t = linear > 0 ? easeFn(linear) : 0;
10988
+ for (let c = 0; c < animLength; c++) {
10989
+ // Each character has its own start/end window based on its reveal order
10990
+ const charStart = charStarts[c];
10991
+ const charEnd = charEnds[c];
10992
+ // Settled zone: character has finished its transition
10993
+ if (t >= charEnd) {
10994
+ if (c < endLength) scrambled += settledText[c];
10995
+ continue;
10996
+ }
10997
+ // Pre-transition zone: reveal wave hasn't reached this character yet
10998
+ if (t <= 0 || t < charStart) {
10999
+ if (hasOverride && c < startLength) {
11000
+ if (hasOverrideChars) {
11001
+ if (startingText[c] === ' ') {
11002
+ scrambled += ' ';
11003
+ } else {
11004
+ if (refreshChars) /** @type {Array<String>} */(overrideCache)[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, totalOverrideChars)];
11005
+ scrambled += /** @type {Array<String>} */(overrideCache)[c];
11006
+ }
11007
+ } else {
11008
+ // Default (override: false): show the original starting text
11009
+ scrambled += startingText[c];
11010
+ }
11011
+ }
11012
+ continue;
11013
+ }
11014
+ // Active zone: character is between charStart and charEnd
11015
+ const isSpace = (c < endLength && settledText[c] === ' ') || (c < startLength && startingText[c] === ' ');
11016
+ if (isSpace) {
11017
+ scrambled += ' ';
11018
+ } else if (hasCursor && t - charStart < cursorZone) {
11019
+ // Cursor sub-zone: show cursor character based on position within cursor width
11020
+ scrambled += cursorChars[cursorLen - 1 - (((t - charStart) / settleSpacing) | 0)];
11021
+ } else {
11022
+ // Scramble zone: show cycling random characters
11023
+ if (refreshChars) charCache[c] = characters[rng(0, totalChars)];
11024
+ scrambled += charCache[c];
11025
+ }
11026
+ }
11027
+ if (refreshChars) onChange(scrambled, t);
11028
+ return scrambled;
11029
+ }
11030
+ }
11031
+ }
11032
+ };
11033
+
10423
11034
  var index = /*#__PURE__*/Object.freeze({
10424
11035
  __proto__: null,
10425
11036
  TextSplitter: TextSplitter,
11037
+ scrambleText: scrambleText,
10426
11038
  split: split,
10427
11039
  splitText: splitText
10428
11040
  });
10429
11041
 
10430
- export { registerTargets as $, Animatable, AutoLayout, Draggable, JSAnimation, Scope, ScrollObserver, Spring, TextSplitter, Timeline, Timer, WAAPIAnimation, animate, clamp, cleanInlineStyles, createAnimatable, createDraggable, createDrawable, createLayout, createMotionPath, createScope, createSeededRandom, createSpring, createTimeline, createTimer, cubicBezier, damp, degToRad, eases, index$3 as easings, engine, get, irregular, keepTime, lerp, linear, mapRange, morphTo, onScroll, padEnd, padStart, radToDeg, random, randomPick, remove, round, roundPad, scrollContainers, set, shuffle, snap, split, splitText, spring, stagger, steps, index$1 as svg, sync, index as text, index$2 as utils, waapi, wrap };
11042
+ export { registerTargets as $, Animatable, AutoLayout, Draggable, JSAnimation, Scope, ScrollObserver, Spring, TextSplitter, Timeline, Timer, WAAPIAnimation, addChild, animate, clamp, cleanInlineStyles, createAnimatable, createDraggable, createDrawable, createLayout, createMotionPath, createScope, createSeededRandom, createSpring, createTimeline, createTimer, cubicBezier, damp, degToRad, eases, index$3 as easings, engine, forEachChildren, get, globals, irregular, keepTime, lerp, linear, mapRange, morphTo, onScroll, padEnd, padStart, radToDeg, random, randomPick, remove, removeChild, round, roundPad, scrambleText, scrollContainers, set, shuffle, snap, split, splitText, spring, stagger, steps, index$1 as svg, sync, index as text, index$2 as utils, waapi, wrap };