animejs 4.3.6 → 4.4.1

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 +6 -9
  2. package/dist/bundles/anime.esm.js +973 -391
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +977 -390
  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 +7 -34
  32. package/dist/modules/core/render.js +8 -35
  33. package/dist/modules/core/styles.cjs +53 -32
  34. package/dist/modules/core/styles.d.ts +1 -0
  35. package/dist/modules/core/styles.js +55 -35
  36. package/dist/modules/core/targets.cjs +1 -1
  37. package/dist/modules/core/targets.js +1 -1
  38. package/dist/modules/core/transforms.cjs +129 -13
  39. package/dist/modules/core/transforms.d.ts +1 -0
  40. package/dist/modules/core/transforms.js +130 -15
  41. package/dist/modules/core/units.cjs +1 -1
  42. package/dist/modules/core/units.js +1 -1
  43. package/dist/modules/core/values.cjs +68 -8
  44. package/dist/modules/core/values.d.ts +5 -2
  45. package/dist/modules/core/values.js +69 -11
  46. package/dist/modules/draggable/draggable.cjs +1 -1
  47. package/dist/modules/draggable/draggable.js +1 -1
  48. package/dist/modules/draggable/index.cjs +1 -1
  49. package/dist/modules/draggable/index.js +1 -1
  50. package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
  51. package/dist/modules/easings/cubic-bezier/index.js +1 -1
  52. package/dist/modules/easings/eases/index.cjs +1 -1
  53. package/dist/modules/easings/eases/index.js +1 -1
  54. package/dist/modules/easings/eases/parser.cjs +1 -1
  55. package/dist/modules/easings/eases/parser.js +1 -1
  56. package/dist/modules/easings/index.cjs +1 -1
  57. package/dist/modules/easings/index.js +1 -1
  58. package/dist/modules/easings/irregular/index.cjs +1 -1
  59. package/dist/modules/easings/irregular/index.js +1 -1
  60. package/dist/modules/easings/linear/index.cjs +1 -1
  61. package/dist/modules/easings/linear/index.js +1 -1
  62. package/dist/modules/easings/none.cjs +1 -1
  63. package/dist/modules/easings/none.js +1 -1
  64. package/dist/modules/easings/spring/index.cjs +1 -1
  65. package/dist/modules/easings/spring/index.js +1 -1
  66. package/dist/modules/easings/steps/index.cjs +1 -1
  67. package/dist/modules/easings/steps/index.js +1 -1
  68. package/dist/modules/engine/engine.cjs +1 -1
  69. package/dist/modules/engine/engine.js +1 -1
  70. package/dist/modules/engine/index.cjs +1 -1
  71. package/dist/modules/engine/index.js +1 -1
  72. package/dist/modules/events/index.cjs +1 -1
  73. package/dist/modules/events/index.js +1 -1
  74. package/dist/modules/events/scroll.cjs +1 -1
  75. package/dist/modules/events/scroll.js +1 -1
  76. package/dist/modules/index.cjs +9 -1
  77. package/dist/modules/index.d.ts +1 -0
  78. package/dist/modules/index.js +4 -1
  79. package/dist/modules/layout/index.cjs +1 -1
  80. package/dist/modules/layout/index.js +1 -1
  81. package/dist/modules/layout/layout.cjs +29 -25
  82. package/dist/modules/layout/layout.d.ts +4 -3
  83. package/dist/modules/layout/layout.js +30 -26
  84. package/dist/modules/scope/index.cjs +1 -1
  85. package/dist/modules/scope/index.js +1 -1
  86. package/dist/modules/scope/scope.cjs +1 -1
  87. package/dist/modules/scope/scope.js +1 -1
  88. package/dist/modules/svg/drawable.cjs +1 -1
  89. package/dist/modules/svg/drawable.js +1 -1
  90. package/dist/modules/svg/helpers.cjs +1 -1
  91. package/dist/modules/svg/helpers.js +1 -1
  92. package/dist/modules/svg/index.cjs +1 -1
  93. package/dist/modules/svg/index.js +1 -1
  94. package/dist/modules/svg/morphto.cjs +3 -6
  95. package/dist/modules/svg/morphto.js +3 -6
  96. package/dist/modules/svg/motionpath.cjs +1 -1
  97. package/dist/modules/svg/motionpath.js +1 -1
  98. package/dist/modules/text/index.cjs +3 -1
  99. package/dist/modules/text/index.d.ts +1 -0
  100. package/dist/modules/text/index.js +2 -1
  101. package/dist/modules/text/scramble.cjs +272 -0
  102. package/dist/modules/text/scramble.d.ts +3 -0
  103. package/dist/modules/text/scramble.js +270 -0
  104. package/dist/modules/text/split.cjs +5 -5
  105. package/dist/modules/text/split.d.ts +5 -5
  106. package/dist/modules/text/split.js +5 -5
  107. package/dist/modules/timeline/index.cjs +1 -1
  108. package/dist/modules/timeline/index.js +1 -1
  109. package/dist/modules/timeline/position.cjs +1 -1
  110. package/dist/modules/timeline/position.js +1 -1
  111. package/dist/modules/timeline/timeline.cjs +36 -18
  112. package/dist/modules/timeline/timeline.d.ts +6 -5
  113. package/dist/modules/timeline/timeline.js +37 -19
  114. package/dist/modules/timer/index.cjs +1 -1
  115. package/dist/modules/timer/index.js +1 -1
  116. package/dist/modules/timer/timer.cjs +8 -12
  117. package/dist/modules/timer/timer.d.ts +2 -0
  118. package/dist/modules/timer/timer.js +9 -13
  119. package/dist/modules/types/index.d.ts +76 -8
  120. package/dist/modules/utils/chainable.cjs +8 -5
  121. package/dist/modules/utils/chainable.js +8 -5
  122. package/dist/modules/utils/index.cjs +5 -1
  123. package/dist/modules/utils/index.d.ts +1 -0
  124. package/dist/modules/utils/index.js +2 -1
  125. package/dist/modules/utils/number.cjs +1 -1
  126. package/dist/modules/utils/number.js +1 -1
  127. package/dist/modules/utils/random.cjs +1 -1
  128. package/dist/modules/utils/random.js +1 -1
  129. package/dist/modules/utils/stagger.cjs +117 -20
  130. package/dist/modules/utils/stagger.js +118 -21
  131. package/dist/modules/utils/target.cjs +1 -1
  132. package/dist/modules/utils/target.js +1 -1
  133. package/dist/modules/utils/time.cjs +5 -3
  134. package/dist/modules/utils/time.d.ts +1 -1
  135. package/dist/modules/utils/time.js +5 -3
  136. package/dist/modules/waapi/composition.cjs +1 -1
  137. package/dist/modules/waapi/composition.js +1 -1
  138. package/dist/modules/waapi/index.cjs +1 -1
  139. package/dist/modules/waapi/index.js +1 -1
  140. package/dist/modules/waapi/waapi.cjs +19 -20
  141. package/dist/modules/waapi/waapi.js +20 -21
  142. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - UMD bundle
3
- * @version v4.3.6
3
+ * @version v4.4.1
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -46,7 +46,8 @@
46
46
  * @callback StaggerFunction
47
47
  * @param {Target} [target]
48
48
  * @param {Number} [index]
49
- * @param {Number} [length]
49
+ * @param {TargetsArray} [targets]
50
+ * @param {Tween|null} [prevTween]
50
51
  * @param {Timeline} [tl]
51
52
  * @return {T}
52
53
  */
@@ -54,9 +55,9 @@
54
55
  /**
55
56
  * @typedef {Object} StaggerParams
56
57
  * @property {Number|String} [start]
57
- * @property {Number|'first'|'center'|'last'|'random'} [from]
58
+ * @property {Number|'first'|'center'|'last'|'random'|Array.<Number>} [from]
58
59
  * @property {Boolean} [reversed]
59
- * @property {Array.<Number>} [grid]
60
+ * @property {Array.<Number>|Boolean} [grid]
60
61
  * @property {('x'|'y')} [axis]
61
62
  * @property {String|((target: Target, i: Number, length: Number) => Number)} [use]
62
63
  * @property {Number} [total]
@@ -117,8 +118,8 @@
117
118
 
118
119
  // A hack to get both ease names suggestions AND allow any strings
119
120
  // https://github.com/microsoft/TypeScript/issues/29729#issuecomment-460346421
120
- /** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring} EasingParam */
121
- /** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring} WAAPIEasingParam */
121
+ /** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring|TweakRegister} EasingParam */
122
+ /** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring|TweakRegister} WAAPIEasingParam */
122
123
 
123
124
  // Spring types
124
125
 
@@ -174,6 +175,7 @@
174
175
  * @property {Boolean|ScrollObserver} [autoplay]
175
176
  * @property {Number} [frameRate]
176
177
  * @property {Number} [playbackRate]
178
+ * @property {Number} [priority]
177
179
  */
178
180
 
179
181
  /**
@@ -184,9 +186,10 @@
184
186
 
185
187
  /**
186
188
  * @callback FunctionValue
187
- * @param {Target} target - The animated target
188
- * @param {Number} index - The target index
189
- * @param {Number} length - The total number of animated targets
189
+ * @param {Target} [target] - The animated target
190
+ * @param {Number} [index] - The target index
191
+ * @param {TargetsArray} [targets] - The array of all animated targets
192
+ * @param {Tween|null} [prevTween] - The previous sibling tween for the same target and property
190
193
  * @return {Number|String|TweenObjectValue|EasingParam|Array.<Number|String|TweenObjectValue>}
191
194
  */
192
195
 
@@ -355,7 +358,7 @@
355
358
  * - `'label'` - Label: Position animation at a named label position (e.g., `'My Label'`)<br>
356
359
  * - `stagger(String|Nummber)` - Stagger multi-elements animation positions (e.g., 10, 20, 30...)
357
360
  *
358
- * @typedef {TimelinePosition | StaggerFunction<Number|String>} TimelineAnimationPosition
361
+ * @typedef {TimelinePosition | StaggerFunction<Number|String> | TweakRegister} TimelineAnimationPosition
359
362
  */
360
363
 
361
364
  /**
@@ -379,7 +382,7 @@
379
382
  * @callback WAAPIFunctionValue
380
383
  * @param {DOMTarget} target - The animated target
381
384
  * @param {Number} index - The target index
382
- * @param {Number} length - The total number of animated targets
385
+ * @param {DOMTargetsArray} targets - The array of all animated targets
383
386
  * @return {WAAPITweenValue|WAAPIEasingParam}
384
387
  */
385
388
 
@@ -626,6 +629,26 @@
626
629
  * @property {Boolean} [debug]
627
630
  */
628
631
 
632
+ /**
633
+ * @typedef {Object} ScrambleTextParams
634
+ * @property {String|function(Target, Number, TargetsArray): String} [text] - the text to transition to, otherwise uses the original text
635
+ * @property {String|function(Target, Number, TargetsArray): String} [chars] - the characters used for scramble; named sets: 'lowercase', 'uppercase', 'numbers', 'symbols', 'braille', 'blocks', 'shades'; range syntax: 'A-Z', 'a-z0-9'; defaults to 'a-zA-Z0-9!%#_'
636
+ * @property {EasingParam} [ease] - the easing applied to the scramble animation
637
+ * @property {Number|'left'|'center'|'right'|'random'|'auto'} [from] - where the reveal wave starts from, 'auto' (default) uses 'left' when text grows and 'right' when it shrinks
638
+ * @property {Boolean} [reversed] - reverses the reveal order, so 'center' reveals from edges inward instead of center outward
639
+ * @property {Boolean|Number|String} [cursor] - characters displayed at the leading edge of the reveal wave; true uses '_', a number is a char code, a string is used directly
640
+ * @property {Number} [perturbation] - adds random timing offsets to each character's start and end, creating a more organic reveal
641
+ * @property {Number} [seed] - a seed for the random number generator to produce reproducible scramble sequences
642
+ * @property {Boolean|String} [override] - controls the starting appearance: false shows original text, true scrambles it (default), '' starts from blank, ' ' replaces characters with spaces, a custom string (supports range syntax like 'A-Z') uses its characters as scramble set
643
+ * @property {Number} [revealRate] - characters per second entering the active zone; higher values make the reveal wave move faster (default: 60)
644
+ * @property {Number} [settleDuration] - time in ms each character spends scrambling before settling into its final glyph (default: 300)
645
+ * @property {Number} [settleRate] - how many times per second scramble characters cycle in the active zone (default: 30)
646
+ * @property {Number|function(Target, Number, TargetsArray): Number} [duration] - if set to a value greater than 0, overrides the computed duration from interval and settle; if unset or 0, duration is calculated automatically from text length and timing parameters
647
+ * @property {Number|function(Target, Number, TargetsArray): Number} [revealDelay] - delay in ms before the reveal wave starts within the scramble animation
648
+ * @property {Number|function(Target, Number, TargetsArray): Number} [delay] - delay in ms before the entire scramble animation starts
649
+ * @property {function(String, Number): void} [onChange] - callback fired each time a character changes during scramble; receives the current scrambled text and the eased progress (0-1)
650
+ */
651
+
629
652
  // SVG types
630
653
 
631
654
  /**
@@ -647,7 +670,7 @@
647
670
  // TODO: Do we need to check if we're running inside a worker ?
648
671
  const isBrowser = typeof window !== 'undefined';
649
672
 
650
- /** @typedef {Window & {AnimeJS: Array} & {AnimeJSDevTools: any}|null} AnimeJSWindow
673
+ /** @typedef {Window & {AnimeJS: Array}|null} AnimeJSWindow
651
674
 
652
675
  /** @type {AnimeJSWindow} */
653
676
  const win = isBrowser ? /** @type {AnimeJSWindow} */(/** @type {unknown} */(window)) : null;
@@ -694,7 +717,6 @@
694
717
  const isDomSymbol = Symbol();
695
718
  const isSvgSymbol = Symbol();
696
719
  const transformsSymbol = Symbol();
697
- const morphPointsSymbol = Symbol();
698
720
  const proxyTargetSymbol = Symbol();
699
721
 
700
722
  // Numbers
@@ -718,6 +740,7 @@
718
740
  })();
719
741
 
720
742
  const validTransforms = [
743
+ 'perspective',
721
744
  'translateX',
722
745
  'translateY',
723
746
  'translateZ',
@@ -732,9 +755,6 @@
732
755
  'skew',
733
756
  'skewX',
734
757
  'skewY',
735
- 'matrix',
736
- 'matrix3d',
737
- 'perspective',
738
758
  ];
739
759
 
740
760
  const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({...a, [v]: v + '('}), {});
@@ -757,12 +777,23 @@
757
777
  // export const unitsExecRgx = /^([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)+([a-z]+|%)$/i;
758
778
  const unitsExecRgx = /^([-+]?\d*\.?\d+(?:e[-+]?\d+)?)([a-z]+|%)$/i;
759
779
  const lowerCaseRgx = /([a-z])([A-Z])/g;
760
- const transformsExecRgx = /(\w+)(\([^)]+\)+)/g; // Match inline transforms with cacl() values, returns the value wrapped in ()
761
780
  const relativeValuesExecRgx = /(\*=|\+=|-=)/;
762
781
  const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/;
763
782
 
764
783
 
765
784
 
785
+ /**
786
+ * @typedef {Object} EditorGlobals
787
+ * @property {boolean} showPanel
788
+ * @property {boolean} synced
789
+ * @property {Function} addAnimation
790
+ * @property {Function} addTimeline
791
+ * @property {Function} addTimelineChild
792
+ * @property {Function} resolveStagger
793
+ * @property {Object|null} _head
794
+ * @property {Object|null} _tail
795
+ */
796
+
766
797
  /** @type {DefaultsParams} */
767
798
  const defaults = {
768
799
  id: null,
@@ -806,11 +837,11 @@
806
837
  timeScale: 1,
807
838
  /** @type {Number} */
808
839
  tickThreshold: 200,
840
+ /** @type {EditorGlobals|null} */
841
+ editor: null,
809
842
  };
810
843
 
811
- const devTools = isBrowser && win.AnimeJSDevTools;
812
-
813
- const globalVersions = { version: '4.3.6', engine: null };
844
+ const globalVersions = { version: '4.4.1', engine: null };
814
845
 
815
846
  if (isBrowser) {
816
847
  if (!win.AnimeJS) win.AnimeJS = [];
@@ -926,8 +957,6 @@
926
957
  */
927
958
  const clamp$1 = (v, min, max) => v < min ? min : v > max ? max : v;
928
959
 
929
- const powCache = {};
930
-
931
960
  /**
932
961
  * Rounds a number to specified decimal places
933
962
  *
@@ -935,13 +964,12 @@
935
964
  * @param {Number} decimalLength - Number of decimal places
936
965
  * @return {Number}
937
966
  */
938
- const round$1 = (v, decimalLength) => {
939
- if (decimalLength < 0) return v;
940
- if (!decimalLength) return _round(v);
941
- let p = powCache[decimalLength];
942
- if (!p) p = powCache[decimalLength] = 10 ** decimalLength;
943
- return _round(v * p) / p;
944
- };
967
+ const round$1 = (v, decimalLength) => {
968
+ if (decimalLength < 0) return v;
969
+ if (!decimalLength) return _round(v);
970
+ const p = 10 ** decimalLength;
971
+ return _round(v * p) / p;
972
+ };
945
973
 
946
974
  /**
947
975
  * Snaps a value to nearest increment or array value
@@ -1072,28 +1100,143 @@
1072
1100
  */
1073
1101
  const parseInlineTransforms = (target, propName, animationInlineStyles) => {
1074
1102
  const inlineTransforms = target.style.transform;
1075
- let inlinedStylesPropertyValue;
1076
1103
  if (inlineTransforms) {
1077
1104
  const cachedTransforms = target[transformsSymbol];
1078
- let t; while (t = transformsExecRgx.exec(inlineTransforms)) {
1079
- const inlinePropertyName = t[1];
1080
- // const inlinePropertyValue = t[2];
1081
- const inlinePropertyValue = t[2].slice(1, -1);
1082
- cachedTransforms[inlinePropertyName] = inlinePropertyValue;
1083
- if (inlinePropertyName === propName) {
1084
- inlinedStylesPropertyValue = inlinePropertyValue;
1085
- // Store the new parsed inline styles if animationInlineStyles is provided
1086
- if (animationInlineStyles) {
1087
- animationInlineStyles[propName] = inlinePropertyValue;
1105
+ let pos = 0;
1106
+ const len = inlineTransforms.length;
1107
+ let fullTranslateValue;
1108
+ while (pos < len) {
1109
+ // Skip whitespace
1110
+ while (pos < len && inlineTransforms.charCodeAt(pos) === 32) pos++;
1111
+ if (pos >= len) break;
1112
+ // Read function name
1113
+ const nameStart = pos;
1114
+ while (pos < len && inlineTransforms.charCodeAt(pos) !== 40) pos++;
1115
+ if (pos >= len) break;
1116
+ const name = inlineTransforms.substring(nameStart, pos);
1117
+ // Scan to closing paren, recording top-level comma positions
1118
+ let depth = 1;
1119
+ const valueStart = pos + 1;
1120
+ let c1 = -1, c2 = -1;
1121
+ pos++;
1122
+ while (pos < len && depth > 0) {
1123
+ const c = inlineTransforms.charCodeAt(pos);
1124
+ if (c === 40) depth++;
1125
+ else if (c === 41) depth--;
1126
+ else if (c === 44 && depth === 1) {
1127
+ if (c1 === -1) c1 = pos;
1128
+ else if (c2 === -1) c2 = pos;
1129
+ }
1130
+ pos++;
1131
+ }
1132
+ const valueEnd = pos - 1;
1133
+ // Decompose multi-arg functions into individual axis properties
1134
+ if (name === 'translate' || name === 'translate3d') {
1135
+ if (c1 === -1) {
1136
+ cachedTransforms.translateX = inlineTransforms.substring(valueStart, valueEnd).trim();
1137
+ } else {
1138
+ cachedTransforms.translateX = inlineTransforms.substring(valueStart, c1).trim();
1139
+ if (c2 === -1) {
1140
+ cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
1141
+ } else {
1142
+ cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, c2).trim();
1143
+ cachedTransforms.translateZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
1144
+ }
1145
+ }
1146
+ fullTranslateValue = inlineTransforms.substring(valueStart, valueEnd);
1147
+ } else if (name === 'scale' || name === 'scale3d') {
1148
+ if (c1 === -1) {
1149
+ cachedTransforms.scale = inlineTransforms.substring(valueStart, valueEnd).trim();
1150
+ } else {
1151
+ cachedTransforms.scaleX = inlineTransforms.substring(valueStart, c1).trim();
1152
+ if (c2 === -1) {
1153
+ cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
1154
+ } else {
1155
+ cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, c2).trim();
1156
+ cachedTransforms.scaleZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
1157
+ }
1088
1158
  }
1159
+ } else {
1160
+ cachedTransforms[name] = inlineTransforms.substring(valueStart, valueEnd);
1089
1161
  }
1090
1162
  }
1163
+ // Resolve the requested property from the cache
1164
+ if (propName === 'translate3d' && fullTranslateValue) {
1165
+ if (animationInlineStyles) animationInlineStyles[propName] = fullTranslateValue;
1166
+ return fullTranslateValue;
1167
+ }
1168
+ const cached = cachedTransforms[propName];
1169
+ if (!isUnd(cached)) {
1170
+ if (animationInlineStyles) animationInlineStyles[propName] = cached;
1171
+ return cached;
1172
+ }
1091
1173
  }
1092
- return inlineTransforms && !isUnd(inlinedStylesPropertyValue) ? inlinedStylesPropertyValue :
1174
+ return propName === 'translate3d' ? '0px, 0px, 0px' :
1175
+ propName === 'rotate3d' ? '0, 0, 0, 0deg' :
1093
1176
  stringStartsWith(propName, 'scale') ? '1' :
1094
1177
  stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew') ? '0deg' : '0px';
1095
1178
  };
1096
1179
 
1180
+ /**
1181
+ * Builds a CSS transform string from the target's cached transform properties.
1182
+ * Iterates validTransforms in order (perspective > translate > rotate > scale > skew > matrix).
1183
+ * When adjacent axis properties are all present, emits a shorter shorthand (translateX + translateY -> translate(x, y))
1184
+ * The index is advanced past consumed properties so they are not emitted twice.
1185
+ * Properties without a grouping partner (e.g. translateY alone, scaleZ alone) emit individually.
1186
+ *
1187
+ * @param {Record<String, String>} props
1188
+ * @return {String}
1189
+ */
1190
+ const buildTransformString = (props) => {
1191
+ let str = emptyString;
1192
+ for (let i = 0, l = validTransforms.length; i < l; i++) {
1193
+ const key = validTransforms[i];
1194
+ const val = props[key];
1195
+ if (val !== undefined) {
1196
+ // Group translateX with adjacent translateY / translateZ
1197
+ if (key === 'translateX') {
1198
+ const next = props.translateY;
1199
+ if (next !== undefined) {
1200
+ const next2 = props.translateZ;
1201
+ if (next2 !== undefined) {
1202
+ str += `translate3d(${val},${next},${next2}) `;
1203
+ i += 2;
1204
+ } else {
1205
+ str += `translate(${val},${next}) `;
1206
+ i += 1;
1207
+ }
1208
+ continue;
1209
+ }
1210
+ }
1211
+ // Group scaleX with adjacent scaleY / scaleZ (only when standalone scale is absent)
1212
+ if (key === 'scaleX' && props.scale === undefined) {
1213
+ const next = props.scaleY;
1214
+ if (next !== undefined) {
1215
+ const next2 = props.scaleZ;
1216
+ if (next2 !== undefined) {
1217
+ str += `scale3d(${val},${next},${next2}) `;
1218
+ i += 2;
1219
+ } else {
1220
+ str += `scale(${val},${next}) `;
1221
+ i += 1;
1222
+ }
1223
+ continue;
1224
+ }
1225
+ }
1226
+ // All other properties: emit individually using pre-built fragment string
1227
+ str += `${transformsFragmentStrings[key]}${val}) `;
1228
+ }
1229
+ // Preserve non-animatable rotate3d in correct position (after rotateZ, before scale)
1230
+ if (key === 'rotateZ') {
1231
+ if (props.rotate3d !== undefined) str += `rotate3d(${props.rotate3d}) `;
1232
+ }
1233
+ }
1234
+ // Preserve non-animatable matrix/matrix3d from inline styles
1235
+ if (props.matrix !== undefined) str += `matrix(${props.matrix}) `;
1236
+ if (props.matrix3d !== undefined) str += `matrix3d(${props.matrix3d}) `;
1237
+ return str;
1238
+ };
1239
+
1097
1240
 
1098
1241
 
1099
1242
  /**
@@ -1195,15 +1338,16 @@
1195
1338
  * @param {TweenPropValue} value
1196
1339
  * @param {Target} target
1197
1340
  * @param {Number} index
1198
- * @param {Number} total
1199
- * @param {Object} [store]
1341
+ * @param {TargetsArray} targets
1342
+ * @param {Object|null} store
1343
+ * @param {Tween|null} prevTween
1200
1344
  * @return {any}
1201
1345
  */
1202
- const getFunctionValue = (value, target, index, total, store) => {
1346
+ const getFunctionValue = (value, target, index, targets, store, prevTween) => {
1203
1347
  let func;
1204
1348
  if (isFnc(value)) {
1205
1349
  func = () => {
1206
- const computed = /** @type {Function} */(value)(target, index, total);
1350
+ const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
1207
1351
  // Fallback to 0 if the function returns undefined / NaN / null / false / 0
1208
1352
  return !isNaN(+computed) ? +computed : computed || 0;
1209
1353
  };
@@ -1270,9 +1414,17 @@
1270
1414
  */
1271
1415
  const getOriginalAnimatableValue = (target, propName, tweenType, animationInlineStyles) => {
1272
1416
  const type = !isUnd(tweenType) ? tweenType : getTweenType(target, propName);
1273
- return type === tweenTypes.OBJECT ? target[propName] || 0 :
1274
- type === tweenTypes.ATTRIBUTE ? /** @type {DOMTarget} */(target).getAttribute(propName) :
1275
- type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
1417
+ if (type === tweenTypes.OBJECT) {
1418
+ const value = target[propName];
1419
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1420
+ return value || 0;
1421
+ }
1422
+ if (type === tweenTypes.ATTRIBUTE) {
1423
+ const value = /** @type {DOMTarget} */(target).getAttribute(propName);
1424
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1425
+ return value;
1426
+ }
1427
+ return type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
1276
1428
  type === tweenTypes.CSS_VAR ? getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles).trimStart() :
1277
1429
  getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles);
1278
1430
  };
@@ -1374,6 +1526,54 @@
1374
1526
 
1375
1527
  const decomposedOriginalValue = createDecomposedValueTargetObject();
1376
1528
 
1529
+ /**
1530
+ * @param {Tween} tween
1531
+ * @param {Number} progress
1532
+ * @param {Number} precision
1533
+ * @return {String}
1534
+ */
1535
+ const composeColorValue = (tween, progress, precision) => {
1536
+ const mod = tween._modifier;
1537
+ const fn = tween._fromNumbers;
1538
+ const tn = tween._toNumbers;
1539
+ const r = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[0], tn[0], progress))), 0, 255), 0);
1540
+ const g = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[1], tn[1], progress))), 0, 255), 0);
1541
+ const b = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[2], tn[2], progress))), 0, 255), 0);
1542
+ const a = clamp$1(/** @type {Number} */(mod(round$1(lerp$1(fn[3], tn[3], progress), precision))), 0, 1);
1543
+ if (tween._composition !== compositionTypes.none) {
1544
+ const ns = tween._numbers;
1545
+ ns[0] = r;
1546
+ ns[1] = g;
1547
+ ns[2] = b;
1548
+ ns[3] = a;
1549
+ }
1550
+ return `rgba(${r},${g},${b},${a})`;
1551
+ };
1552
+
1553
+ /**
1554
+ * @param {Tween} tween
1555
+ * @param {Number} progress
1556
+ * @param {Number} precision
1557
+ * @return {String}
1558
+ */
1559
+ const composeComplexValue = (tween, progress, precision) => {
1560
+ const mod = tween._modifier;
1561
+ const fn = tween._fromNumbers;
1562
+ const tn = tween._toNumbers;
1563
+ const ts = tween._strings;
1564
+ const hasComposition = tween._composition !== compositionTypes.none;
1565
+ let v = ts[0];
1566
+ for (let j = 0, l = tn.length; j < l; j++) {
1567
+ const n = /** @type {Number} */(mod(round$1(lerp$1(fn[j], tn[j], progress), precision)));
1568
+ const s = ts[j + 1];
1569
+ v += `${s ? n + s : n}`;
1570
+ if (hasComposition) {
1571
+ tween._numbers[j] = n;
1572
+ }
1573
+ }
1574
+ return v;
1575
+ };
1576
+
1377
1577
 
1378
1578
 
1379
1579
 
@@ -1402,7 +1602,6 @@
1402
1602
  const _hasChildren = tickable._hasChildren;
1403
1603
  const tickableDelay = tickable._delay;
1404
1604
  const tickablePrevAbsoluteTime = tickable._currentTime; // TODO: rename ._currentTime to ._absoluteCurrentTime
1405
-
1406
1605
  const tickableEndTime = tickableDelay + iterationDuration;
1407
1606
  const tickableAbsoluteTime = time - tickableDelay;
1408
1607
  const tickablePrevTime = clamp$1(tickablePrevAbsoluteTime, -tickableDelay, duration);
@@ -1534,30 +1733,9 @@
1534
1733
  number = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
1535
1734
  value = `${number}${tween._unit}`;
1536
1735
  } else if (tweenValueType === valueTypes.COLOR) {
1537
- const fn = tween._fromNumbers;
1538
- const tn = tween._toNumbers;
1539
- const r = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[0], tn[0], tweenProgress))), 0, 255), 0);
1540
- const g = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[1], tn[1], tweenProgress))), 0, 255), 0);
1541
- const b = round$1(clamp$1(/** @type {Number} */(tweenModifier(lerp$1(fn[2], tn[2], tweenProgress))), 0, 255), 0);
1542
- const a = clamp$1(/** @type {Number} */(tweenModifier(round$1(lerp$1(fn[3], tn[3], tweenProgress), tweenPrecision))), 0, 1);
1543
- value = `rgba(${r},${g},${b},${a})`;
1544
- if (tweenHasComposition) {
1545
- const ns = tween._numbers;
1546
- ns[0] = r;
1547
- ns[1] = g;
1548
- ns[2] = b;
1549
- ns[3] = a;
1550
- }
1736
+ value = composeColorValue(tween, tweenProgress, tweenPrecision);
1551
1737
  } else if (tweenValueType === valueTypes.COMPLEX) {
1552
- value = tween._strings[0];
1553
- for (let j = 0, l = tween._toNumbers.length; j < l; j++) {
1554
- const n = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumbers[j], tween._toNumbers[j], tweenProgress), tweenPrecision)));
1555
- const s = tween._strings[j + 1];
1556
- value += `${s ? n + s : n}`;
1557
- if (tweenHasComposition) {
1558
- tween._numbers[j] = n;
1559
- }
1560
- }
1738
+ value = composeComplexValue(tween, tweenProgress, tweenPrecision);
1561
1739
  }
1562
1740
 
1563
1741
  // For additive tweens and Animatables
@@ -1600,14 +1778,8 @@
1600
1778
 
1601
1779
  }
1602
1780
 
1603
- // NOTE: Possible improvement: Use translate(x,y) / translate3d(x,y,z) syntax
1604
- // to reduce memory usage on string composition
1605
1781
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
1606
- let str = emptyString;
1607
- for (let key in tweenTargetTransformsProperties) {
1608
- str += `${transformsFragmentStrings[key]}${tweenTargetTransformsProperties[key]}) `;
1609
- }
1610
- tweenStyle.transform = str;
1782
+ tweenStyle.transform = buildTransformString(tweenTargetTransformsProperties);
1611
1783
  tweenTransformsNeedUpdate = 0;
1612
1784
  }
1613
1785
 
@@ -1718,7 +1890,6 @@
1718
1890
 
1719
1891
  // Renders on timeline are triggered by its children so it needs to be set after rendering the children
1720
1892
  if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
1721
-
1722
1893
  // Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
1723
1894
  if ((tlChildrenHaveCompleted || tlIsRunningBackwards) && tl._currentTime >= tl.duration) {
1724
1895
  // Make sure the paused flag is false in case it has been skipped in the render function
@@ -1772,59 +1943,78 @@
1772
1943
  /**
1773
1944
  * @template {Renderable} T
1774
1945
  * @param {T} renderable
1946
+ * @param {Boolean} [inlineStylesOnly]
1775
1947
  * @return {T}
1776
1948
  */
1777
- const cleanInlineStyles = renderable => {
1778
- // Allow cleanInlineStyles() to be called on timelines
1949
+ const revertValues = (renderable, inlineStylesOnly = false) => {
1950
+ // Allow revertValues() to be called on timelines
1779
1951
  if (renderable._hasChildren) {
1780
- forEachChildren(renderable, cleanInlineStyles, true);
1952
+ forEachChildren(renderable, (/** @type {Renderable} */child) => revertValues(child, inlineStylesOnly), true);
1781
1953
  } else {
1782
1954
  const animation = /** @type {JSAnimation} */(renderable);
1783
1955
  animation.pause();
1784
1956
  forEachChildren(animation, (/** @type {Tween} */tween) => {
1785
1957
  const tweenProperty = tween.property;
1786
1958
  const tweenTarget = tween.target;
1787
- if (tweenTarget[isDomSymbol]) {
1788
- const targetStyle = /** @type {DOMTarget} */(tweenTarget).style;
1789
- const originalInlinedValue = tween._inlineValue;
1790
- const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1791
- if (tween._tweenType === tweenTypes.TRANSFORM) {
1792
- const cachedTransforms = tweenTarget[transformsSymbol];
1793
- if (tweenHadNoInlineValue) {
1794
- delete cachedTransforms[tweenProperty];
1795
- } else {
1796
- cachedTransforms[tweenProperty] = originalInlinedValue;
1797
- }
1798
- if (tween._renderTransforms) {
1799
- if (!Object.keys(cachedTransforms).length) {
1800
- targetStyle.removeProperty('transform');
1959
+ const tweenType = tween._tweenType;
1960
+ const originalInlinedValue = tween._inlineValue;
1961
+ const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1962
+ if (tweenType === tweenTypes.OBJECT) {
1963
+ if (!inlineStylesOnly && !tweenHadNoInlineValue) {
1964
+ tweenTarget[tweenProperty] = originalInlinedValue;
1965
+ }
1966
+ } else if (tweenTarget[isDomSymbol]) {
1967
+ if (tweenType === tweenTypes.ATTRIBUTE) {
1968
+ if (!inlineStylesOnly) {
1969
+ if (tweenHadNoInlineValue) {
1970
+ /** @type {DOMTarget} */(tweenTarget).removeAttribute(tweenProperty);
1801
1971
  } else {
1802
- let str = emptyString;
1803
- for (let key in cachedTransforms) {
1804
- str += transformsFragmentStrings[key] + cachedTransforms[key] + ') ';
1805
- }
1806
- targetStyle.transform = str;
1972
+ /** @type {DOMTarget} */(tweenTarget).setAttribute(tweenProperty, /** @type {String} */(originalInlinedValue));
1807
1973
  }
1808
1974
  }
1809
1975
  } else {
1810
- if (tweenHadNoInlineValue) {
1811
- targetStyle.removeProperty(toLowerCase(tweenProperty));
1976
+ const targetStyle = /** @type {DOMTarget} */(tweenTarget).style;
1977
+ if (tweenType === tweenTypes.TRANSFORM) {
1978
+ const cachedTransforms = tweenTarget[transformsSymbol];
1979
+ if (tweenHadNoInlineValue) {
1980
+ delete cachedTransforms[tweenProperty];
1981
+ } else {
1982
+ cachedTransforms[tweenProperty] = originalInlinedValue;
1983
+ }
1984
+ if (tween._renderTransforms) {
1985
+ if (!Object.keys(cachedTransforms).length) {
1986
+ targetStyle.removeProperty('transform');
1987
+ } else {
1988
+ targetStyle.transform = buildTransformString(cachedTransforms);
1989
+ }
1990
+ }
1812
1991
  } else {
1813
- targetStyle[tweenProperty] = originalInlinedValue;
1992
+ if (tweenHadNoInlineValue) {
1993
+ targetStyle.removeProperty(toLowerCase(tweenProperty));
1994
+ } else {
1995
+ targetStyle[tweenProperty] = originalInlinedValue;
1996
+ }
1814
1997
  }
1815
1998
  }
1816
- if (animation._tail === tween) {
1817
- animation.targets.forEach(t => {
1818
- if (t.getAttribute && t.getAttribute('style') === emptyString) {
1819
- t.removeAttribute('style');
1820
- } });
1821
- }
1999
+ }
2000
+ if (tweenTarget[isDomSymbol] && animation._tail === tween) {
2001
+ animation.targets.forEach(t => {
2002
+ if (t.getAttribute && t.getAttribute('style') === emptyString) {
2003
+ t.removeAttribute('style');
2004
+ } });
1822
2005
  }
1823
2006
  });
1824
2007
  }
1825
2008
  return renderable;
1826
2009
  };
1827
2010
 
2011
+ /**
2012
+ * @template {Renderable} T
2013
+ * @param {T} renderable
2014
+ * @return {T}
2015
+ */
2016
+ const cleanInlineStyles = renderable => revertValues(renderable, true);
2017
+
1828
2018
 
1829
2019
 
1830
2020
  /*
@@ -2509,6 +2699,9 @@
2509
2699
 
2510
2700
  let timerId = 0;
2511
2701
 
2702
+ /** @param {Timer} prev @param {Timer} child */
2703
+ const sortByPriority = (prev, child) => prev._priority > child._priority;
2704
+
2512
2705
  /**
2513
2706
  * Base class used to create Timers, Animations and Timelines
2514
2707
  */
@@ -2535,6 +2728,7 @@
2535
2728
  autoplay,
2536
2729
  frameRate,
2537
2730
  playbackRate,
2731
+ priority,
2538
2732
  onComplete,
2539
2733
  onLoop,
2540
2734
  onPause,
@@ -2556,16 +2750,6 @@
2556
2750
  /** @type {Number} */(timerLoop) < 0 ? Infinity :
2557
2751
  /** @type {Number} */(timerLoop) + 1;
2558
2752
 
2559
- if (devTools) {
2560
- const isInfinite = timerIterationCount === Infinity;
2561
- const registered = devTools.register(this, parameters, isInfinite);
2562
- if (registered && isInfinite) {
2563
- const minIterations = alternate ? 2 : 1;
2564
- const iterations = parent ? devTools.maxNestedInfiniteLoops : devTools.maxInfiniteLoops;
2565
- timerIterationCount = Math.max(iterations, minIterations);
2566
- }
2567
- }
2568
-
2569
2753
  let offsetPosition = 0;
2570
2754
 
2571
2755
  if (parent) {
@@ -2649,6 +2833,8 @@
2649
2833
  this._fps = setValue(frameRate, timerDefaults.frameRate);
2650
2834
  /** @type {Number} */
2651
2835
  this._speed = setValue(playbackRate, timerDefaults.playbackRate);
2836
+ /** @type {Number} */
2837
+ this._priority = +setValue(priority, 1);
2652
2838
  }
2653
2839
 
2654
2840
  get cancelled() {
@@ -2793,7 +2979,7 @@
2793
2979
  tick(this, minValue, 0, 0, tickModes.FORCE);
2794
2980
  } else {
2795
2981
  if (!this._running) {
2796
- addChild(engine, this);
2982
+ addChild(engine, this, sortByPriority);
2797
2983
  engine._hasChildren = true;
2798
2984
  this._running = true;
2799
2985
  }
@@ -3388,7 +3574,7 @@
3388
3574
  * @param {Number} [parentPosition]
3389
3575
  * @param {Boolean} [fastSet=false]
3390
3576
  * @param {Number} [index=0]
3391
- * @param {Number} [length=0]
3577
+ * @param {TargetsArray} [allTargets]
3392
3578
  */
3393
3579
  constructor(
3394
3580
  targets,
@@ -3397,7 +3583,7 @@
3397
3583
  parentPosition,
3398
3584
  fastSet = false,
3399
3585
  index = 0,
3400
- length = 0
3586
+ allTargets
3401
3587
  ) {
3402
3588
 
3403
3589
  super(/** @type {TimerParams & AnimationParams} */(parameters), parent, parentPosition);
@@ -3448,7 +3634,7 @@
3448
3634
 
3449
3635
  const target = parsedTargets[targetIndex];
3450
3636
  const ti = index || targetIndex;
3451
- const tl = length || targetsLength;
3637
+ const tl = allTargets || parsedTargets;
3452
3638
 
3453
3639
  let lastTransformGroupIndex = NaN;
3454
3640
  let lastTransformGroupLength = NaN;
@@ -3524,7 +3710,14 @@
3524
3710
  toFunctionStore.func = null;
3525
3711
  fromFunctionStore.func = null;
3526
3712
 
3527
- const computedToValue = getFunctionValue(key.to, target, ti, tl, toFunctionStore);
3713
+ const computedComposition = getFunctionValue(setValue(key.composition, tComposition), target, ti, tl, null, null);
3714
+ const tweenComposition = isNum(computedComposition) ? computedComposition : compositionTypes[computedComposition];
3715
+ if (!siblings && tweenComposition !== compositionTypes.none) siblings = getTweenSiblings(target, propName);
3716
+ // Timelines pass the last sibling tween if it belongs to the same timeline
3717
+ // Standalone animations only pass prevTween when the property has multiple keyframes
3718
+ const tailTween = siblings ? siblings._tail : null;
3719
+ const prevSiblingTween = parent && tailTween && tailTween.parent.parent === parent ? tailTween : prevTween;
3720
+ const computedToValue = getFunctionValue(key.to, target, ti, tl, toFunctionStore, prevSiblingTween);
3528
3721
 
3529
3722
  let tweenToValue;
3530
3723
  // Allows function based values to return an object syntax value ({to: v})
@@ -3534,20 +3727,18 @@
3534
3727
  } else {
3535
3728
  tweenToValue = computedToValue;
3536
3729
  }
3537
- const tweenFromValue = getFunctionValue(key.from, target, ti, tl);
3730
+ const tweenFromValue = getFunctionValue(key.from, target, ti, tl, null, prevSiblingTween);
3538
3731
  const easeToParse = key.ease || tEasing;
3539
3732
 
3540
- const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl);
3733
+ const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl, null, prevSiblingTween);
3541
3734
  const keyEasing = isFnc(easeFunctionResult) || isStr(easeFunctionResult) ? easeFunctionResult : easeToParse;
3542
3735
 
3543
3736
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
3544
3737
  const tweenEasing = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
3545
3738
  // Calculate default individual keyframe duration by dividing the tl of keyframes
3546
- const tweenDuration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(setValue(key.duration, (l > 1 ? getFunctionValue(tDuration, target, ti, tl) / l : tDuration)), target, ti, tl);
3739
+ const tweenDuration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(setValue(key.duration, (l > 1 ? getFunctionValue(tDuration, target, ti, tl, null, prevSiblingTween) / l : tDuration)), target, ti, tl, null, prevSiblingTween);
3547
3740
  // Default delay value should only be applied to the first tween
3548
- const tweenDelay = getFunctionValue(setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl);
3549
- const computedComposition = getFunctionValue(setValue(key.composition, tComposition), target, ti, tl);
3550
- const tweenComposition = isNum(computedComposition) ? computedComposition : compositionTypes[computedComposition];
3741
+ const tweenDelay = getFunctionValue(setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl, null, prevSiblingTween);
3551
3742
  // Modifiers are treated differently and don't accept function based value to prevent having to pass a function wrapper
3552
3743
  const tweenModifier = key.modifier || tModifier;
3553
3744
  const hasFromvalue = !isUnd(tweenFromValue);
@@ -3564,7 +3755,6 @@
3564
3755
  let prevSibling = prevTween;
3565
3756
 
3566
3757
  if (tweenComposition !== compositionTypes.none) {
3567
- if (!siblings) siblings = getTweenSiblings(target, propName);
3568
3758
  let nextSibling = siblings._head;
3569
3759
  // Iterate trough all the next siblings until we find a sibling with an equal or inferior start time
3570
3760
  while (nextSibling && !nextSibling._isOverridden && nextSibling._absoluteStartTime <= absoluteStartTime) {
@@ -3583,8 +3773,8 @@
3583
3773
 
3584
3774
  // Decompose values
3585
3775
  if (isFromToValue) {
3586
- decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore) : tweenFromValue, fromTargetObject);
3587
- decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore) : tweenToValue, toTargetObject);
3776
+ decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore, prevSiblingTween) : tweenFromValue, fromTargetObject);
3777
+ decomposeRawValue(isFromToArray ? getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore, prevSiblingTween) : tweenToValue, toTargetObject);
3588
3778
  // Needed to force an inline style registration
3589
3779
  const originalValue = getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore);
3590
3780
  if (fromTargetObject.t === valueTypes.NUMBER) {
@@ -3738,6 +3928,18 @@
3738
3928
  composeTween(tween, siblings);
3739
3929
  }
3740
3930
 
3931
+ // Pre-compute the tween end value for function-based value chaining (ie morphTo / scrambleText in keyframe arrays and timelines)
3932
+ const vt = tween._valueType;
3933
+ if (vt === valueTypes.COMPLEX) {
3934
+ tween._value = composeComplexValue(tween, 1, -1);
3935
+ } else if (vt === valueTypes.COLOR) {
3936
+ tween._value = composeColorValue(tween, 1, -1);
3937
+ } else if (vt === valueTypes.UNIT) {
3938
+ tween._value = `${tweenModifier(tween._toNumber)}${tween._unit}`;
3939
+ } else {
3940
+ tween._value = tweenModifier(tween._toNumber);
3941
+ }
3942
+
3741
3943
  if (isNaN(firstTweenChangeStartTime)) {
3742
3944
  firstTweenChangeStartTime = tween._startTime;
3743
3945
  }
@@ -3895,7 +4097,7 @@
3895
4097
  */
3896
4098
  revert() {
3897
4099
  super.revert();
3898
- return cleanInlineStyles(this);
4100
+ return revertValues(this);
3899
4101
  }
3900
4102
 
3901
4103
  /**
@@ -3917,170 +4119,12 @@
3917
4119
  * @param {AnimationParams} parameters
3918
4120
  * @return {JSAnimation}
3919
4121
  */
3920
- const animate = (targets, parameters) => new JSAnimation(targets, parameters, null, 0, false).init();
3921
-
3922
-
3923
-
3924
-
3925
-
3926
- const WAAPIAnimationsLookups = {
3927
- _head: null,
3928
- _tail: null,
3929
- };
3930
-
3931
- /**
3932
- * @param {DOMTarget} $el
3933
- * @param {String} [property]
3934
- * @param {WAAPIAnimation} [parent]
3935
- * @return {globalThis.Animation}
3936
- */
3937
- const removeWAAPIAnimation = ($el, property, parent) => {
3938
- let nextLookup = WAAPIAnimationsLookups._head;
3939
- let anim;
3940
- while (nextLookup) {
3941
- const next = nextLookup._next;
3942
- const matchTarget = nextLookup.$el === $el;
3943
- const matchProperty = !property || nextLookup.property === property;
3944
- const matchParent = !parent || nextLookup.parent === parent;
3945
- if (matchTarget && matchProperty && matchParent) {
3946
- anim = nextLookup.animation;
3947
- try { anim.commitStyles(); } catch {} anim.cancel();
3948
- removeChild(WAAPIAnimationsLookups, nextLookup);
3949
- const lookupParent = nextLookup.parent;
3950
- if (lookupParent) {
3951
- lookupParent._completed++;
3952
- if (lookupParent.animations.length === lookupParent._completed) {
3953
- lookupParent.completed = true;
3954
- lookupParent.paused = true;
3955
- if (!lookupParent.muteCallbacks) {
3956
- lookupParent.onComplete(lookupParent);
3957
- lookupParent._resolve(lookupParent);
3958
- }
3959
- }
3960
- }
3961
- }
3962
- nextLookup = next;
3963
- }
3964
- return anim;
3965
- };
3966
-
3967
- /**
3968
- * @param {WAAPIAnimation} parent
3969
- * @param {DOMTarget} $el
3970
- * @param {String} property
3971
- * @param {PropertyIndexedKeyframes} keyframes
3972
- * @param {KeyframeAnimationOptions} params
3973
- * @retun {globalThis.Animation}
3974
- */
3975
- const addWAAPIAnimation = (parent, $el, property, keyframes, params) => {
3976
- const animation = $el.animate(keyframes, params);
3977
- const animTotalDuration = params.delay + (+params.duration * params.iterations);
3978
- animation.playbackRate = parent._speed;
3979
- if (parent.paused) animation.pause();
3980
- if (parent.duration < animTotalDuration) {
3981
- parent.duration = animTotalDuration;
3982
- parent.controlAnimation = animation;
3983
- }
3984
- parent.animations.push(animation);
3985
- removeWAAPIAnimation($el, property);
3986
- addChild(WAAPIAnimationsLookups, { parent, animation, $el, property, _next: null, _prev: null });
3987
- const handleRemove = () => removeWAAPIAnimation($el, property, parent);
3988
- animation.oncancel = handleRemove;
3989
- animation.onremove = handleRemove;
3990
- if (!parent.persist) {
3991
- animation.onfinish = handleRemove;
3992
- }
3993
- return animation;
3994
- };
3995
-
3996
-
3997
-
3998
-
3999
-
4000
- /**
4001
- * @overload
4002
- * @param {DOMTargetSelector} targetSelector
4003
- * @param {String} propName
4004
- * @return {String}
4005
- *
4006
- * @overload
4007
- * @param {JSTargetsParam} targetSelector
4008
- * @param {String} propName
4009
- * @return {Number|String}
4010
- *
4011
- * @overload
4012
- * @param {DOMTargetsParam} targetSelector
4013
- * @param {String} propName
4014
- * @param {String} unit
4015
- * @return {String}
4016
- *
4017
- * @overload
4018
- * @param {TargetsParam} targetSelector
4019
- * @param {String} propName
4020
- * @param {Boolean} unit
4021
- * @return {Number}
4022
- *
4023
- * @param {TargetsParam} targetSelector
4024
- * @param {String} propName
4025
- * @param {String|Boolean} [unit]
4026
- */
4027
- function get(targetSelector, propName, unit) {
4028
- const targets = registerTargets(targetSelector);
4029
- if (!targets.length) return;
4030
- const [ target ] = targets;
4031
- const tweenType = getTweenType(target, propName);
4032
- const normalizePropName = sanitizePropertyName(propName, target, tweenType);
4033
- let originalValue = getOriginalAnimatableValue(target, normalizePropName);
4034
- if (isUnd(unit)) {
4035
- return originalValue;
4122
+ const animate = (targets, parameters) => {
4123
+ if (globals.editor) {
4124
+ return globals.editor.addAnimation(targets, parameters);
4036
4125
  } else {
4037
- decomposeRawValue(originalValue, decomposedOriginalValue);
4038
- if (decomposedOriginalValue.t === valueTypes.NUMBER || decomposedOriginalValue.t === valueTypes.UNIT) {
4039
- if (unit === false) {
4040
- return decomposedOriginalValue.n;
4041
- } else {
4042
- const convertedValue = convertValueUnit(/** @type {DOMTarget} */(target), decomposedOriginalValue, /** @type {String} */(unit), false);
4043
- return `${round$1(convertedValue.n, globals.precision)}${convertedValue.u}`;
4044
- }
4045
- }
4126
+ return new JSAnimation(targets, parameters, null, 0, false).init();
4046
4127
  }
4047
- }
4048
-
4049
- /**
4050
- * @param {TargetsParam} targets
4051
- * @param {AnimationParams} parameters
4052
- * @return {JSAnimation}
4053
- */
4054
- const set = (targets, parameters) => {
4055
- if (isUnd(parameters)) return;
4056
- parameters.duration = minValue;
4057
- // Do not overrides currently active tweens by default
4058
- parameters.composition = setValue(parameters.composition, compositionTypes.none);
4059
- // Skip init() and force rendering by playing the animation
4060
- return new JSAnimation(targets, parameters, null, 0, true).resume();
4061
- };
4062
-
4063
- /**
4064
- * @param {TargetsParam} targets
4065
- * @param {Renderable|WAAPIAnimation} [renderable]
4066
- * @param {String} [propertyName]
4067
- * @return {TargetsArray}
4068
- */
4069
- const remove = (targets, renderable, propertyName) => {
4070
- const targetsArray = parseTargets(targets);
4071
- for (let i = 0, l = targetsArray.length; i < l; i++) {
4072
- removeWAAPIAnimation(
4073
- /** @type {DOMTarget} */(targetsArray[i]),
4074
- propertyName,
4075
- renderable && /** @type {WAAPIAnimation} */(renderable).controlAnimation && /** @type {WAAPIAnimation} */(renderable),
4076
- );
4077
- }
4078
- removeTargetsFromRenderable(
4079
- targetsArray,
4080
- /** @type {Renderable} */(renderable),
4081
- propertyName
4082
- );
4083
- return targetsArray;
4084
4128
  };
4085
4129
 
4086
4130
 
@@ -4136,6 +4180,8 @@
4136
4180
 
4137
4181
 
4138
4182
 
4183
+
4184
+
4139
4185
  /**
4140
4186
  * @param {Timeline} tl
4141
4187
  * @return {Number}
@@ -4157,7 +4203,7 @@
4157
4203
  * @param {Number} timePosition
4158
4204
  * @param {TargetsParam} targets
4159
4205
  * @param {Number} [index]
4160
- * @param {Number} [length]
4206
+ * @param {TargetsArray} [allTargets]
4161
4207
  * @return {Timeline}
4162
4208
  *
4163
4209
  * @param {TimerParams|AnimationParams} childParams
@@ -4165,15 +4211,15 @@
4165
4211
  * @param {Number} timePosition
4166
4212
  * @param {TargetsParam} [targets]
4167
4213
  * @param {Number} [index]
4168
- * @param {Number} [length]
4214
+ * @param {TargetsArray} [allTargets]
4169
4215
  */
4170
- function addTlChild(childParams, tl, timePosition, targets, index, length) {
4216
+ function addTlChild(childParams, tl, timePosition, targets, index, allTargets) {
4171
4217
  const isSetter = isNum(childParams.duration) && /** @type {Number} */(childParams.duration) <= minValue;
4172
4218
  // Offset the tl position with -minValue for 0 duration animations or .set() calls in order to align their end value with the defined position
4173
4219
  const adjustedPosition = isSetter ? timePosition - minValue : timePosition;
4174
4220
  if (tl.composition) tick(tl, adjustedPosition, 1, 1, tickModes.AUTO);
4175
4221
  const tlChild = targets ?
4176
- new JSAnimation(targets,/** @type {AnimationParams} */(childParams), tl, adjustedPosition, false, index, length) :
4222
+ new JSAnimation(targets,/** @type {AnimationParams} */(childParams), tl, adjustedPosition, false, index, allTargets) :
4177
4223
  new Timer(/** @type {TimerParams} */(childParams), tl, adjustedPosition);
4178
4224
  if (tl.composition) tlChild.init(true);
4179
4225
  // TODO: Might be better to insert at a position relative to startTime?
@@ -4221,7 +4267,7 @@
4221
4267
  * @overload
4222
4268
  * @param {TargetsParam} a1
4223
4269
  * @param {AnimationParams} a2
4224
- * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4270
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [a3]
4225
4271
  * @return {this}
4226
4272
  *
4227
4273
  * @overload
@@ -4231,7 +4277,7 @@
4231
4277
  *
4232
4278
  * @param {TargetsParam|TimerParams} a1
4233
4279
  * @param {TimelinePosition|AnimationParams} a2
4234
- * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4280
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [a3]
4235
4281
  */
4236
4282
  add(a1, a2, a3) {
4237
4283
  const isAnim = isObj(a2);
@@ -4240,9 +4286,11 @@
4240
4286
  this._hasChildren = true;
4241
4287
  if (isAnim) {
4242
4288
  const childParams = /** @type {AnimationParams} */(a2);
4243
- // Check for function for children stagger positions
4244
- if (isFnc(a3)) {
4245
- const staggeredPosition = a3;
4289
+ const editorHook = globals.editor && globals.editor.addTimelineChild;
4290
+ const isStaggerType = a3 && /** @type {TweakRegister} */(a3).type === 'Stagger' && globals.editor;
4291
+ // Check for function or Stagger type children positions
4292
+ const staggeredPosition = isFnc(a3) ? a3 : null;
4293
+ if (staggeredPosition || isStaggerType) {
4246
4294
  const parsedTargetsArray = parseTargets(/** @type {TargetsParam} */(a1));
4247
4295
  // Store initial duration before adding new children that will change the duration
4248
4296
  const tlDuration = this.duration;
@@ -4253,28 +4301,36 @@
4253
4301
  let i = 0;
4254
4302
  /** @type {Number} */
4255
4303
  const parsedLength = (parsedTargetsArray.length);
4304
+ // Call editor hook once for the entire stagger group instead of per target
4305
+ const resolvedParams = editorHook ? editorHook(/** @type {TargetsParam} */(a1), childParams, this.id, a3, parsedLength) : null;
4306
+ // Resolve stagger AFTER editor hook so tweaked position value (a3.defaultValue) is used
4307
+ const staggerFn = staggeredPosition || globals.editor.resolveStagger(/** @type {TweakRegister} */(a3).defaultValue);
4256
4308
  parsedTargetsArray.forEach((/** @type {Target} */target) => {
4257
4309
  // Create a new parameter object for each staggered children
4258
- const staggeredChildParams = { ...childParams };
4310
+ const staggeredChildParams = { ...(resolvedParams || childParams) };
4259
4311
  // Reset the duration of the timeline iteration before each stagger to prevent wrong start value calculation
4260
4312
  this.duration = tlDuration;
4261
4313
  this.iterationDuration = tlIterationDuration;
4262
4314
  if (!isUnd(id)) staggeredChildParams.id = id + '-' + i;
4315
+ const staggeredTimePosition = parseTimelinePosition(this, staggerFn(target, i, parsedTargetsArray, null, this));
4263
4316
  addTlChild(
4264
4317
  staggeredChildParams,
4265
4318
  this,
4266
- parseTimelinePosition(this, staggeredPosition(target, i, parsedLength, this)),
4319
+ staggeredTimePosition,
4267
4320
  target,
4268
4321
  i,
4269
- parsedLength
4322
+ parsedTargetsArray,
4270
4323
  );
4271
4324
  i++;
4272
4325
  });
4273
4326
  } else {
4327
+ // Call editor hook before resolving position so tweaked values are applied
4328
+ const resolvedChildParams = editorHook ? editorHook(/** @type {TargetsParam} */(a1), childParams, this.id, a3) : childParams;
4329
+ const resolvedPosition = a3 && /** @type {*} */(a3).type ? /** @type {*} */(a3).defaultValue : a3;
4274
4330
  addTlChild(
4275
- childParams,
4331
+ resolvedChildParams,
4276
4332
  this,
4277
- parseTimelinePosition(this, a3),
4333
+ parseTimelinePosition(this, resolvedPosition),
4278
4334
  /** @type {TargetsParam} */(a1),
4279
4335
  );
4280
4336
  }
@@ -4396,7 +4452,7 @@
4396
4452
  revert() {
4397
4453
  super.revert();
4398
4454
  forEachChildren(this, (/** @type {JSAnimation|Timer} */child) => child.revert, true);
4399
- return cleanInlineStyles(this);
4455
+ return revertValues(this);
4400
4456
  }
4401
4457
 
4402
4458
  /**
@@ -4416,7 +4472,12 @@
4416
4472
  * @param {TimelineParams} [parameters]
4417
4473
  * @return {Timeline}
4418
4474
  */
4419
- const createTimeline = parameters => new Timeline(parameters).init();
4475
+ const createTimeline = parameters => {
4476
+ if (globals.editor) {
4477
+ return /** @type {Timeline} */(/** @type {unknown} */(globals.editor.addTimeline(parameters)));
4478
+ }
4479
+ return new Timeline(parameters).init();
4480
+ };
4420
4481
 
4421
4482
 
4422
4483
 
@@ -4863,6 +4924,170 @@
4863
4924
 
4864
4925
 
4865
4926
 
4927
+ const WAAPIAnimationsLookups = {
4928
+ _head: null,
4929
+ _tail: null,
4930
+ };
4931
+
4932
+ /**
4933
+ * @param {DOMTarget} $el
4934
+ * @param {String} [property]
4935
+ * @param {WAAPIAnimation} [parent]
4936
+ * @return {globalThis.Animation}
4937
+ */
4938
+ const removeWAAPIAnimation = ($el, property, parent) => {
4939
+ let nextLookup = WAAPIAnimationsLookups._head;
4940
+ let anim;
4941
+ while (nextLookup) {
4942
+ const next = nextLookup._next;
4943
+ const matchTarget = nextLookup.$el === $el;
4944
+ const matchProperty = !property || nextLookup.property === property;
4945
+ const matchParent = !parent || nextLookup.parent === parent;
4946
+ if (matchTarget && matchProperty && matchParent) {
4947
+ anim = nextLookup.animation;
4948
+ try { anim.commitStyles(); } catch {} anim.cancel();
4949
+ removeChild(WAAPIAnimationsLookups, nextLookup);
4950
+ const lookupParent = nextLookup.parent;
4951
+ if (lookupParent) {
4952
+ lookupParent._completed++;
4953
+ if (lookupParent.animations.length === lookupParent._completed) {
4954
+ lookupParent.completed = true;
4955
+ lookupParent.paused = true;
4956
+ if (!lookupParent.muteCallbacks) {
4957
+ lookupParent.onComplete(lookupParent);
4958
+ lookupParent._resolve(lookupParent);
4959
+ }
4960
+ }
4961
+ }
4962
+ }
4963
+ nextLookup = next;
4964
+ }
4965
+ return anim;
4966
+ };
4967
+
4968
+ /**
4969
+ * @param {WAAPIAnimation} parent
4970
+ * @param {DOMTarget} $el
4971
+ * @param {String} property
4972
+ * @param {PropertyIndexedKeyframes} keyframes
4973
+ * @param {KeyframeAnimationOptions} params
4974
+ * @retun {globalThis.Animation}
4975
+ */
4976
+ const addWAAPIAnimation = (parent, $el, property, keyframes, params) => {
4977
+ const animation = $el.animate(keyframes, params);
4978
+ const animTotalDuration = params.delay + (+params.duration * params.iterations);
4979
+ animation.playbackRate = parent._speed;
4980
+ if (parent.paused) animation.pause();
4981
+ if (parent.duration < animTotalDuration) {
4982
+ parent.duration = animTotalDuration;
4983
+ parent.controlAnimation = animation;
4984
+ }
4985
+ parent.animations.push(animation);
4986
+ removeWAAPIAnimation($el, property);
4987
+ addChild(WAAPIAnimationsLookups, { parent, animation, $el, property, _next: null, _prev: null });
4988
+ const handleRemove = () => removeWAAPIAnimation($el, property, parent);
4989
+ animation.oncancel = handleRemove;
4990
+ animation.onremove = handleRemove;
4991
+ if (!parent.persist) {
4992
+ animation.onfinish = handleRemove;
4993
+ }
4994
+ return animation;
4995
+ };
4996
+
4997
+
4998
+
4999
+
5000
+
5001
+ /**
5002
+ * @overload
5003
+ * @param {DOMTargetSelector} targetSelector
5004
+ * @param {String} propName
5005
+ * @return {String}
5006
+ *
5007
+ * @overload
5008
+ * @param {JSTargetsParam} targetSelector
5009
+ * @param {String} propName
5010
+ * @return {Number|String}
5011
+ *
5012
+ * @overload
5013
+ * @param {DOMTargetsParam} targetSelector
5014
+ * @param {String} propName
5015
+ * @param {String} unit
5016
+ * @return {String}
5017
+ *
5018
+ * @overload
5019
+ * @param {TargetsParam} targetSelector
5020
+ * @param {String} propName
5021
+ * @param {Boolean} unit
5022
+ * @return {Number}
5023
+ *
5024
+ * @param {TargetsParam} targetSelector
5025
+ * @param {String} propName
5026
+ * @param {String|Boolean} [unit]
5027
+ */
5028
+ function get(targetSelector, propName, unit) {
5029
+ const targets = registerTargets(targetSelector);
5030
+ if (!targets.length) return;
5031
+ const [ target ] = targets;
5032
+ const tweenType = getTweenType(target, propName);
5033
+ const normalizePropName = sanitizePropertyName(propName, target, tweenType);
5034
+ let originalValue = getOriginalAnimatableValue(target, normalizePropName);
5035
+ if (isUnd(unit)) {
5036
+ return originalValue;
5037
+ } else {
5038
+ decomposeRawValue(originalValue, decomposedOriginalValue);
5039
+ if (decomposedOriginalValue.t === valueTypes.NUMBER || decomposedOriginalValue.t === valueTypes.UNIT) {
5040
+ if (unit === false) {
5041
+ return decomposedOriginalValue.n;
5042
+ } else {
5043
+ const convertedValue = convertValueUnit(/** @type {DOMTarget} */(target), decomposedOriginalValue, /** @type {String} */(unit), false);
5044
+ return `${round$1(convertedValue.n, globals.precision)}${convertedValue.u}`;
5045
+ }
5046
+ }
5047
+ }
5048
+ }
5049
+
5050
+ /**
5051
+ * @param {TargetsParam} targets
5052
+ * @param {AnimationParams} parameters
5053
+ * @return {JSAnimation}
5054
+ */
5055
+ const set = (targets, parameters) => {
5056
+ if (isUnd(parameters)) return;
5057
+ parameters.duration = minValue;
5058
+ // Do not overrides currently active tweens by default
5059
+ parameters.composition = setValue(parameters.composition, compositionTypes.none);
5060
+ // Skip init() and force rendering by playing the animation
5061
+ return new JSAnimation(targets, parameters, null, 0, true).resume();
5062
+ };
5063
+
5064
+ /**
5065
+ * @param {TargetsParam} targets
5066
+ * @param {Renderable|WAAPIAnimation} [renderable]
5067
+ * @param {String} [propertyName]
5068
+ * @return {TargetsArray}
5069
+ */
5070
+ const remove = (targets, renderable, propertyName) => {
5071
+ const targetsArray = parseTargets(targets);
5072
+ for (let i = 0, l = targetsArray.length; i < l; i++) {
5073
+ removeWAAPIAnimation(
5074
+ /** @type {DOMTarget} */(targetsArray[i]),
5075
+ propertyName,
5076
+ renderable && /** @type {WAAPIAnimation} */(renderable).controlAnimation && /** @type {WAAPIAnimation} */(renderable),
5077
+ );
5078
+ }
5079
+ removeTargetsFromRenderable(
5080
+ targetsArray,
5081
+ /** @type {Renderable} */(renderable),
5082
+ propertyName
5083
+ );
5084
+ return targetsArray;
5085
+ };
5086
+
5087
+
5088
+
5089
+
5090
+
4866
5091
  /**
4867
5092
  * @param {Event} e
4868
5093
  */
@@ -6065,19 +6290,20 @@
6065
6290
  };
6066
6291
 
6067
6292
  /**
6068
- * @param {(...args: any[]) => Tickable | ((...args: any[]) => void)} constructor
6293
+ * @param {(...args: any[]) => Tickable | ((...args: any[]) => void) | void} constructor
6069
6294
  * @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
6070
6295
  */
6071
6296
  const keepTime = constructor => {
6072
6297
  /** @type {Tickable} */
6073
6298
  let tracked;
6074
6299
  return (...args) => {
6075
- let currentIteration, currentIterationProgress, reversed, alternate;
6300
+ let currentIteration, currentIterationProgress, reversed, alternate, startTime;
6076
6301
  if (tracked) {
6077
6302
  currentIteration = tracked.currentIteration;
6078
6303
  currentIterationProgress = tracked.iterationProgress;
6079
6304
  reversed = tracked.reversed;
6080
6305
  alternate = tracked._alternate;
6306
+ startTime = tracked._startTime;
6081
6307
  tracked.revert();
6082
6308
  }
6083
6309
  const cleanup = constructor(...args);
@@ -6085,6 +6311,7 @@
6085
6311
  if (!isUnd(currentIterationProgress)) {
6086
6312
  /** @type {Tickable} */(tracked).currentIteration = currentIteration;
6087
6313
  /** @type {Tickable} */(tracked).iterationProgress = (alternate ? !(currentIteration % 2) ? reversed : !reversed : reversed) ? 1 - currentIterationProgress : currentIterationProgress;
6314
+ /** @type {Tickable} */(tracked)._startTime = startTime;
6088
6315
  }
6089
6316
  return cleanup || noop;
6090
6317
  }
@@ -7430,12 +7657,12 @@
7430
7657
  * @param {WAAPIKeyframeValue} value
7431
7658
  * @param {DOMTarget} $el
7432
7659
  * @param {Number} i
7433
- * @param {Number} targetsLength
7660
+ * @param {DOMTargetsArray} parsedTargets
7434
7661
  * @return {String}
7435
7662
  */
7436
- const normalizeTweenValue = (propName, value, $el, i, targetsLength) => {
7663
+ const normalizeTweenValue = (propName, value, $el, i, parsedTargets) => {
7437
7664
  // Do not try to compute strings with getFunctionValue otherwise it will convert CSS variables
7438
- let v = isStr(value) ? value : getFunctionValue(/** @type {any} */(value), $el, i, targetsLength);
7665
+ let v = isStr(value) ? value : getFunctionValue(/** @type {any} */(value), $el, i, parsedTargets, null, null);
7439
7666
  if (!isNum(v)) return v;
7440
7667
  if (commonDefaultPXProperties.includes(propName) || stringStartsWith(propName, 'translate')) return `${v}px`;
7441
7668
  if (stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew')) return `${v}deg`;
@@ -7448,18 +7675,18 @@
7448
7675
  * @param {WAAPIKeyframeValue} from
7449
7676
  * @param {WAAPIKeyframeValue} to
7450
7677
  * @param {Number} i
7451
- * @param {Number} targetsLength
7678
+ * @param {DOMTargetsArray} parsedTargets
7452
7679
  * @return {WAAPITweenValue}
7453
7680
  */
7454
- const parseIndividualTweenValue = ($el, propName, from, to, i, targetsLength) => {
7681
+ const parseIndividualTweenValue = ($el, propName, from, to, i, parsedTargets) => {
7455
7682
  /** @type {WAAPITweenValue} */
7456
7683
  let tweenValue = '0';
7457
- const computedTo = !isUnd(to) ? normalizeTweenValue(propName, to, $el, i, targetsLength) : getComputedStyle($el)[propName];
7684
+ const computedTo = !isUnd(to) ? normalizeTweenValue(propName, to, $el, i, parsedTargets) : getComputedStyle($el)[propName];
7458
7685
  if (!isUnd(from)) {
7459
- const computedFrom = normalizeTweenValue(propName, from, $el, i, targetsLength);
7686
+ const computedFrom = normalizeTweenValue(propName, from, $el, i, parsedTargets);
7460
7687
  tweenValue = [computedFrom, computedTo];
7461
7688
  } else {
7462
- tweenValue = isArr(to) ? to.map((/** @type {any} */v) => normalizeTweenValue(propName, v, $el, i, targetsLength)) : computedTo;
7689
+ tweenValue = isArr(to) ? to.map((/** @type {any} */v) => normalizeTweenValue(propName, v, $el, i, parsedTargets)) : computedTo;
7463
7690
  }
7464
7691
  return tweenValue;
7465
7692
  };
@@ -7498,9 +7725,8 @@
7498
7725
  }
7499
7726
 
7500
7727
  const parsedTargets = registerTargets(targets);
7501
- const targetsLength = parsedTargets.length;
7502
7728
 
7503
- if (!targetsLength) {
7729
+ if (!parsedTargets.length) {
7504
7730
  console.warn(`No target found. Make sure the element you're trying to animate is accessible before creating your animation.`);
7505
7731
  }
7506
7732
 
@@ -7556,7 +7782,7 @@
7556
7782
 
7557
7783
  const easeToParse = setValue(params.ease, globals.defaults.ease);
7558
7784
 
7559
- const easeFunctionResult = getFunctionValue(easeToParse, $el, i, targetsLength);
7785
+ const easeFunctionResult = getFunctionValue(easeToParse, $el, i, parsedTargets, null, null);
7560
7786
  const keyEasing = isFnc(easeFunctionResult) || isStr(easeFunctionResult) ? easeFunctionResult : easeToParse;
7561
7787
 
7562
7788
  const spring = /** @type {Spring} */(easeToParse).ease && easeToParse;
@@ -7564,9 +7790,9 @@
7564
7790
  const easing = parseWAAPIEasing(keyEasing);
7565
7791
 
7566
7792
  /** @type {Number} */
7567
- const duration = (spring ? /** @type {Spring} */(spring).settlingDuration : getFunctionValue(setValue(params.duration, globals.defaults.duration), $el, i, targetsLength)) * timeScale;
7793
+ const duration = (spring ? /** @type {Spring} */(spring).settlingDuration : getFunctionValue(setValue(params.duration, globals.defaults.duration), $el, i, parsedTargets, null, null)) * timeScale;
7568
7794
  /** @type {Number} */
7569
- const delay = getFunctionValue(setValue(params.delay, globals.defaults.delay), $el, i, targetsLength) * timeScale;
7795
+ const delay = getFunctionValue(setValue(params.delay, globals.defaults.delay), $el, i, parsedTargets, null, null) * timeScale;
7570
7796
  /** @type {CompositeOperation} */
7571
7797
  const composite = /** @type {CompositeOperation} */(setValue(params.composition, 'replace'));
7572
7798
 
@@ -7592,19 +7818,19 @@
7592
7818
  const to = /** @type {WAAPITweenOptions} */(tweenOptions).to;
7593
7819
  const from = /** @type {WAAPITweenOptions} */(tweenOptions).from;
7594
7820
  /** @type {Number} */
7595
- tweenParams.duration = (tweenOptionsSpring ? /** @type {Spring} */(tweenOptionsSpring).settlingDuration : getFunctionValue(setValue(tweenOptions.duration, duration), $el, i, targetsLength)) * timeScale;
7821
+ tweenParams.duration = (tweenOptionsSpring ? /** @type {Spring} */(tweenOptionsSpring).settlingDuration : getFunctionValue(setValue(tweenOptions.duration, duration), $el, i, parsedTargets, null, null)) * timeScale;
7596
7822
  /** @type {Number} */
7597
- tweenParams.delay = getFunctionValue(setValue(tweenOptions.delay, delay), $el, i, targetsLength) * timeScale;
7823
+ tweenParams.delay = getFunctionValue(setValue(tweenOptions.delay, delay), $el, i, parsedTargets, null, null) * timeScale;
7598
7824
  /** @type {CompositeOperation} */
7599
7825
  tweenParams.composite = /** @type {CompositeOperation} */(setValue(tweenOptions.composition, composite));
7600
7826
  /** @type {String} */
7601
7827
  tweenParams.easing = parseWAAPIEasing(tweenOptionsEase);
7602
- parsedPropertyValue = parseIndividualTweenValue($el, name, from, to, i, targetsLength);
7828
+ parsedPropertyValue = parseIndividualTweenValue($el, name, from, to, i, parsedTargets);
7603
7829
  if (individualTransformProperty) {
7604
7830
  keyframes[`--${individualTransformProperty}`] = parsedPropertyValue;
7605
7831
  cachedTransforms[individualTransformProperty] = parsedPropertyValue;
7606
7832
  } else {
7607
- keyframes[name] = parseIndividualTweenValue($el, name, from, to, i, targetsLength);
7833
+ keyframes[name] = parseIndividualTweenValue($el, name, from, to, i, parsedTargets);
7608
7834
  }
7609
7835
  addWAAPIAnimation(this, $el, name, keyframes, tweenParams);
7610
7836
  if (!isUnd(from)) {
@@ -7617,8 +7843,8 @@
7617
7843
  }
7618
7844
  } else {
7619
7845
  parsedPropertyValue = isArr(propertyValue) ?
7620
- propertyValue.map((/** @type {any} */v) => normalizeTweenValue(name, v, $el, i, targetsLength)) :
7621
- normalizeTweenValue(name, /** @type {any} */(propertyValue), $el, i, targetsLength);
7846
+ propertyValue.map((/** @type {any} */v) => normalizeTweenValue(name, v, $el, i, parsedTargets)) :
7847
+ normalizeTweenValue(name, /** @type {any} */(propertyValue), $el, i, parsedTargets);
7622
7848
  if (individualTransformProperty) {
7623
7849
  keyframes[`--${individualTransformProperty}`] = parsedPropertyValue;
7624
7850
  cachedTransforms[individualTransformProperty] = parsedPropertyValue;
@@ -7851,6 +8077,7 @@
7851
8077
 
7852
8078
  /**
7853
8079
  * @typedef {Object} LayoutSpecificAnimationParams
8080
+ * @property {Number|String} [id]
7854
8081
  * @property {Number|FunctionValue} [delay]
7855
8082
  * @property {Number|FunctionValue} [duration]
7856
8083
  * @property {EasingParam|FunctionValue} [ease]
@@ -7893,7 +8120,7 @@
7893
8120
  * @property {String} id
7894
8121
  * @property {DOMTarget} $el
7895
8122
  * @property {Number} index
7896
- * @property {Number} total
8123
+ * @property {Array<DOMTarget>} targets
7897
8124
  * @property {Number} delay
7898
8125
  * @property {Number} duration
7899
8126
  * @property {EasingParam} ease
@@ -8037,7 +8264,7 @@
8037
8264
  node.$measure = $el;
8038
8265
  node.id = dataId;
8039
8266
  node.index = 0;
8040
- node.total = 1;
8267
+ node.targets = null;
8041
8268
  node.delay = 0;
8042
8269
  node.duration = 0;
8043
8270
  node.ease = null;
@@ -8223,12 +8450,12 @@
8223
8450
  * @param {LayoutAnimationTimingsParams} params
8224
8451
  */
8225
8452
  const updateNodeTimingParams = (node, params) => {
8226
- const easeFunctionResult = getFunctionValue(params.ease, node.$el, node.index, node.total);
8453
+ const easeFunctionResult = getFunctionValue(params.ease, node.$el, node.index, node.targets, null, null);
8227
8454
  const keyEasing = isFnc(easeFunctionResult) ? easeFunctionResult : params.ease;
8228
8455
  const hasSpring = !isUnd(keyEasing) && !isUnd(/** @type {Spring} */(keyEasing).ease);
8229
8456
  node.ease = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing;
8230
- node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.total);
8231
- node.delay = getFunctionValue(params.delay, node.$el, node.index, node.total);
8457
+ node.duration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : getFunctionValue(params.duration, node.$el, node.index, node.targets, null, null);
8458
+ node.delay = getFunctionValue(params.delay, node.$el, node.index, node.targets, null, null);
8232
8459
  };
8233
8460
 
8234
8461
  /**
@@ -8601,10 +8828,12 @@
8601
8828
 
8602
8829
  const inRootNodeIds = new Set();
8603
8830
  // Update index and total for inital timing calculation
8604
- let index = 0, total = this.nodes.size;
8831
+ let index = 0;
8832
+ const allNodeTargets = [];
8833
+ this.nodes.forEach((node) => { allNodeTargets.push(node.$el); });
8605
8834
  this.nodes.forEach((node, id) => {
8606
8835
  node.index = index++;
8607
- node.total = total;
8836
+ node.targets = allNodeTargets;
8608
8837
  // Track ids of nodes that belong to the current root to filter detached matches
8609
8838
  if (node && node.measuredIsInsideRoot) {
8610
8839
  inRootNodeIds.add(id);
@@ -8716,8 +8945,8 @@
8716
8945
  this.params = params;
8717
8946
  /** @type {DOMTarget} */
8718
8947
  this.root = /** @type {DOMTarget} */(registerTargets(root)[0]);
8719
- /** @type {Number} */
8720
- this.id = layoutId++;
8948
+ /** @type {Number|String} */
8949
+ this.id = params.id || layoutId++;
8721
8950
  /** @type {LayoutChildrenParam} */
8722
8951
  this.children = params.children || '*';
8723
8952
  /** @type {Boolean} */
@@ -8840,7 +9069,9 @@
8840
9069
  duration: setValue(params.duration, this.params.duration),
8841
9070
  };
8842
9071
  /** @type {TimelineParams} */
8843
- const tlParams = {};
9072
+ const tlParams = {
9073
+ id: this.id
9074
+ };
8844
9075
  const onComplete = setValue(params.onComplete, this.params.onComplete);
8845
9076
  const onPause = setValue(params.onPause, this.params.onPause);
8846
9077
  for (let name in defaults) {
@@ -8854,7 +9085,8 @@
8854
9085
  }
8855
9086
  tlParams.onComplete = () => {
8856
9087
  const ap = /** @type {ScrollObserver} */(params.autoplay);
8857
- const isScrollControled = ap && ap.linked;
9088
+ const ed = globals.editor;
9089
+ const isScrollControled = (ap && ap.linked) || (ed && ed.showPanel);
8858
9090
  if (isScrollControled) {
8859
9091
  if (onComplete) onComplete(this.timeline);
8860
9092
  return;
@@ -9053,41 +9285,39 @@
9053
9285
  animatedParent = animatedParent.parentNode;
9054
9286
  }
9055
9287
 
9056
- const animatingTotal = animating.length;
9057
-
9058
9288
  // Root is always animated first in sync with the first child (animating.length is the total of children)
9059
9289
  if (node === rootNode) {
9060
9290
  node.index = 0;
9061
- node.total = animatingTotal;
9291
+ node.targets = animating;
9062
9292
  updateNodeTimingParams(node, animationTimings);
9063
9293
  } else if (node.isEntering) {
9064
9294
  node.index = animatedParent ? animatedParent.index : enteringIndex;
9065
- node.total = animatedParent ? animatingTotal : entering.length;
9295
+ node.targets = animatedParent ? animating : entering;
9066
9296
  updateNodeTimingParams(node, enterFromTimings);
9067
9297
  enteringIndex++;
9068
9298
  } else if (node.isLeaving) {
9069
9299
  node.index = animatedParent ? animatedParent.index : leavingIndex;
9070
- node.total = animatedParent ? animatingTotal : leaving.length;
9300
+ node.targets = animatedParent ? animating : leaving;
9071
9301
  leavingIndex++;
9072
9302
  updateNodeTimingParams(node, leaveToTimings);
9073
9303
  } else if (node.isTarget) {
9074
9304
  node.index = animatingIndex++;
9075
- node.total = animatingTotal;
9305
+ node.targets = animating;
9076
9306
  updateNodeTimingParams(node, animationTimings);
9077
9307
  } else {
9078
9308
  node.index = animatedParent ? animatedParent.index : 0;
9079
- node.total = animatingTotal;
9309
+ node.targets = animating;
9080
9310
  updateNodeTimingParams(node, swapAtTimings);
9081
9311
  }
9082
9312
 
9083
9313
  // Make sure the old state node has its inex and total values up to date for valid "from" function values calculation
9084
9314
  oldStateNode.index = node.index;
9085
- oldStateNode.total = node.total;
9315
+ oldStateNode.targets = node.targets;
9086
9316
 
9087
9317
  // Computes all values up front so we can check for changes and we don't have to re-compute them inside the animation props
9088
9318
  for (let prop in nodeProperties) {
9089
- nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.total);
9090
- oldStateNodeProperties[prop] = getFunctionValue(oldStateNodeProperties[prop], $el, oldStateNode.index, oldStateNode.total);
9319
+ nodeProperties[prop] = getFunctionValue(nodeProperties[prop], $el, node.index, node.targets, null, null);
9320
+ oldStateNodeProperties[prop] = getFunctionValue(oldStateNodeProperties[prop], $el, oldStateNode.index, oldStateNode.targets, null, null);
9091
9321
  }
9092
9322
 
9093
9323
  // Use a 1px tolerance to detect dimensions changes to prevent width / height animations on barelly visible elements
@@ -9313,8 +9543,8 @@
9313
9543
  }
9314
9544
  $el.style.transform = oldState.getComputedValue($el, 'transform');
9315
9545
  if (animatedSwap.includes($el)) {
9316
- node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.total);
9317
- node.duration = getFunctionValue(swapAtParams.duration, $el, node.index, node.total);
9546
+ node.ease = getFunctionValue(swapAtParams.ease, $el, node.index, node.targets, null, null);
9547
+ node.duration = getFunctionValue(swapAtParams.duration, $el, node.index, node.targets, null, null);
9318
9548
  }
9319
9549
  }
9320
9550
  this.transformAnimation = waapi.animate(transformed, {
@@ -9329,7 +9559,7 @@
9329
9559
  if (!animatedSwap.includes($el)) return newValue;
9330
9560
  const oldValue = oldState.getComputedValue($el, 'transform');
9331
9561
  const node = newState.getNode($el);
9332
- return [oldValue, getFunctionValue(swapAtProps.transform, $el, node.index, node.total), newValue]
9562
+ return [oldValue, getFunctionValue(swapAtProps.transform, $el, node.index, node.targets, null, null), newValue]
9333
9563
  },
9334
9564
  autoplay: false,
9335
9565
  // persist: true,
@@ -9386,10 +9616,13 @@
9386
9616
  const result = fn(...args);
9387
9617
  return new Proxy(noop, {
9388
9618
  apply: (_, __, [v]) => result(v),
9389
- get: (_, prop) => chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9390
- const nextResult = chainables[prop](...nextArgs);
9391
- return (/**@type {Number|String} */v) => nextResult(result(v));
9392
- })
9619
+ get: (_, prop) => {
9620
+ if (!chainables[prop]) return undefined;
9621
+ return chain(/**@param {...Number|String} nextArgs */(...nextArgs) => {
9622
+ const nextResult = chainables[prop](...nextArgs);
9623
+ return (/**@type {Number|String} */v) => nextResult(result(v));
9624
+ })
9625
+ }
9393
9626
  });
9394
9627
  }
9395
9628
  };
@@ -9600,24 +9833,28 @@
9600
9833
  * @param {StaggerParams} [params]
9601
9834
  * @return {StaggerFunction<Number>}
9602
9835
  */
9836
+
9603
9837
  /**
9604
9838
  * @overload
9605
9839
  * @param {String} val
9606
9840
  * @param {StaggerParams} [params]
9607
9841
  * @return {StaggerFunction<String>}
9608
9842
  */
9843
+
9609
9844
  /**
9610
9845
  * @overload
9611
9846
  * @param {[Number, Number]} val
9612
9847
  * @param {StaggerParams} [params]
9613
9848
  * @return {StaggerFunction<Number>}
9614
9849
  */
9850
+
9615
9851
  /**
9616
9852
  * @overload
9617
9853
  * @param {[String, String]} val
9618
9854
  * @param {StaggerParams} [params]
9619
9855
  * @return {StaggerFunction<String>}
9620
9856
  */
9857
+
9621
9858
  /**
9622
9859
  * @param {Number|String|[Number, Number]|[String, String]} val The staggered value or range
9623
9860
  * @param {StaggerParams} [params] The stagger parameters
@@ -9626,6 +9863,7 @@
9626
9863
  const stagger = (val, params = {}) => {
9627
9864
  let values = [];
9628
9865
  let maxValue = 0;
9866
+ let cachedOffset;
9629
9867
  const from = params.from;
9630
9868
  const reversed = params.reversed;
9631
9869
  const ease = params.ease;
@@ -9633,12 +9871,14 @@
9633
9871
  const hasSpring = hasEasing && !isUnd(/** @type {Spring} */(ease).ease);
9634
9872
  const staggerEase = hasSpring ? /** @type {Spring} */(ease).ease : hasEasing ? parseEase(ease) : null;
9635
9873
  const grid = params.grid;
9874
+ const autoGrid = grid === true;
9636
9875
  const axis = params.axis;
9637
9876
  const customTotal = params.total;
9638
9877
  const fromFirst = isUnd(from) || from === 0 || from === 'first';
9639
9878
  const fromCenter = from === 'center';
9640
9879
  const fromLast = from === 'last';
9641
9880
  const fromRandom = from === 'random';
9881
+ const fromArr = isArr(from);
9642
9882
  const isRange = isArr(val);
9643
9883
  const useProp = params.use;
9644
9884
  const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
@@ -9646,40 +9886,129 @@
9646
9886
  const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
9647
9887
  const start = params.start || 0 + (isRange ? val1 : 0);
9648
9888
  let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
9649
- return (target, i, t, tl) => {
9889
+ return (target, i, t, _, tl) => {
9650
9890
  const [ registeredTarget ] = registerTargets(target);
9651
- const total = isUnd(customTotal) ? t : customTotal;
9891
+ const total = isUnd(customTotal) ? t.length : customTotal;
9652
9892
  const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
9653
9893
  const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
9654
9894
  if (fromCenter) fromIndex = (total - 1) / 2;
9655
9895
  if (fromLast) fromIndex = total - 1;
9656
9896
  if (!values.length) {
9657
- for (let index = 0; index < total; index++) {
9658
- if (!grid) {
9659
- values.push(abs(fromIndex - index));
9897
+ if (autoGrid) {
9898
+ let hasPositions = true;
9899
+ let minPosX = Infinity;
9900
+ let minPosY = Infinity;
9901
+ let maxPosX = -Infinity;
9902
+ let maxPosY = -Infinity;
9903
+ const pxArr = [];
9904
+ const pyArr = [];
9905
+ for (let index = 0; index < total; index++) {
9906
+ const el = t[index];
9907
+ let px = 0;
9908
+ let py = 0;
9909
+ let found = false;
9910
+ if (el && isFnc(el.getBoundingClientRect)) {
9911
+ const rect = el.getBoundingClientRect();
9912
+ px = rect.left + rect.width / 2;
9913
+ py = rect.top + rect.height / 2;
9914
+ found = true;
9915
+ } else {
9916
+ const obj = /** @type {JSTarget} */(el);
9917
+ if (obj && isNum(obj.x) && isNum(obj.y)) {
9918
+ px = obj.x;
9919
+ py = obj.y;
9920
+ found = true;
9921
+ }
9922
+ }
9923
+ if (!found) {
9924
+ hasPositions = false;
9925
+ break;
9926
+ }
9927
+ pxArr.push(px);
9928
+ pyArr.push(py);
9929
+ if (px < minPosX) minPosX = px;
9930
+ if (py < minPosY) minPosY = py;
9931
+ if (px > maxPosX) maxPosX = px;
9932
+ if (py > maxPosY) maxPosY = py;
9933
+ }
9934
+ if (hasPositions) {
9935
+ let fX = pxArr[0];
9936
+ let fY = pyArr[0];
9937
+ if (fromArr) {
9938
+ fX = minPosX + from[0] * (maxPosX - minPosX);
9939
+ fY = minPosY + from[1] * (maxPosY - minPosY);
9940
+ } else if (fromCenter) {
9941
+ fX = (minPosX + maxPosX) / 2;
9942
+ fY = (minPosY + maxPosY) / 2;
9943
+ } else if (fromLast) {
9944
+ fX = pxArr[total - 1];
9945
+ fY = pyArr[total - 1];
9946
+ } else if (isNum(from)) {
9947
+ fX = pxArr[from];
9948
+ fY = pyArr[from];
9949
+ }
9950
+ for (let index = 0; index < total; index++) {
9951
+ const distanceX = fX - pxArr[index];
9952
+ const distanceY = fY - pyArr[index];
9953
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9954
+ if (axis === 'x') value = -distanceX;
9955
+ if (axis === 'y') value = -distanceY;
9956
+ values.push(value);
9957
+ }
9958
+ let minDist = Infinity;
9959
+ for (let index = 0, l = values.length; index < l; index++) {
9960
+ const absVal = abs(values[index]);
9961
+ if (absVal > 0 && absVal < minDist) minDist = absVal;
9962
+ }
9963
+ if (minDist > 0 && minDist < Infinity) {
9964
+ for (let index = 0, l = values.length; index < l; index++) {
9965
+ values[index] = values[index] / minDist;
9966
+ }
9967
+ }
9660
9968
  } else {
9661
- const fromX = !fromCenter ? fromIndex % grid[0] : (grid[0] - 1) / 2;
9662
- const fromY = !fromCenter ? floor(fromIndex / grid[0]) : (grid[1] - 1) / 2;
9663
- const toX = index % grid[0];
9664
- const toY = floor(index / grid[0]);
9665
- const distanceX = fromX - toX;
9666
- const distanceY = fromY - toY;
9667
- let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9668
- if (axis === 'x') value = -distanceX;
9669
- if (axis === 'y') value = -distanceY;
9670
- values.push(value);
9969
+ for (let index = 0; index < total; index++) {
9970
+ values.push(abs(fromIndex - index));
9971
+ }
9972
+ }
9973
+ } else {
9974
+ for (let index = 0; index < total; index++) {
9975
+ if (!grid) {
9976
+ values.push(abs(fromIndex - index));
9977
+ } else {
9978
+ let fromX, fromY;
9979
+ if (fromArr) {
9980
+ fromX = from[0] * (grid[0] - 1);
9981
+ fromY = from[1] * (grid[1] - 1);
9982
+ } else if (fromCenter) {
9983
+ fromX = (grid[0] - 1) / 2;
9984
+ fromY = (grid[1] - 1) / 2;
9985
+ } else {
9986
+ fromX = fromIndex % grid[0];
9987
+ fromY = floor(fromIndex / grid[0]);
9988
+ }
9989
+ const toX = index % grid[0];
9990
+ const toY = floor(index / grid[0]);
9991
+ const distanceX = fromX - toX;
9992
+ const distanceY = fromY - toY;
9993
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY);
9994
+ if (axis === 'x') value = -distanceX;
9995
+ if (axis === 'y') value = -distanceY;
9996
+ values.push(value);
9997
+ }
9671
9998
  }
9672
- maxValue = max(...values);
9673
9999
  }
10000
+ maxValue = max(...values);
9674
10001
  if (staggerEase) values = values.map(val => staggerEase(val / maxValue) * maxValue);
9675
10002
  if (reversed) values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
9676
10003
  if (fromRandom) values = shuffle(values);
9677
10004
  }
9678
10005
  const spacing = isRange ? (val2 - val1) / maxValue : val1;
9679
- const offset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
10006
+ if (isUnd(cachedOffset)) {
10007
+ cachedOffset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
10008
+ }
9680
10009
  /** @type {String|Number} */
9681
- let output = offset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
9682
- if (params.modifier) output = params.modifier(output);
10010
+ let output = cachedOffset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
10011
+ if (params.modifier) output = params.modifier(/** @type {Number} */(output));
9683
10012
  if (unitMatch) output = `${output}${unitMatch[2]}`;
9684
10013
  return output;
9685
10014
  }
@@ -9688,11 +10017,13 @@
9688
10017
  var index$2 = /*#__PURE__*/Object.freeze({
9689
10018
  __proto__: null,
9690
10019
  $: registerTargets,
10020
+ addChild: addChild,
9691
10021
  clamp: clamp,
9692
10022
  cleanInlineStyles: cleanInlineStyles,
9693
10023
  createSeededRandom: createSeededRandom,
9694
10024
  damp: damp,
9695
10025
  degToRad: degToRad,
10026
+ forEachChildren: forEachChildren,
9696
10027
  get: get,
9697
10028
  keepTime: keepTime,
9698
10029
  lerp: lerp,
@@ -9703,6 +10034,7 @@
9703
10034
  random: random,
9704
10035
  randomPick: randomPick,
9705
10036
  remove: remove,
10037
+ removeChild: removeChild,
9706
10038
  round: round,
9707
10039
  roundPad: roundPad,
9708
10040
  set: set,
@@ -9902,7 +10234,7 @@
9902
10234
  * @param {Number} [precision]
9903
10235
  * @return {FunctionValue}
9904
10236
  */
9905
- const morphTo = (path2, precision = .33) => ($path1) => {
10237
+ const morphTo = (path2, precision = .33) => ($path1, index, total, prevTween) => {
9906
10238
  const tagName1 = ($path1.tagName || '').toLowerCase();
9907
10239
  if (!tagName1.match(/^(path|polygon|polyline)$/)) {
9908
10240
  throw new Error(`Can't morph a <${$path1.tagName}> SVG element. Use <path>, <polygon> or <polyline>.`);
@@ -9917,7 +10249,7 @@
9917
10249
  }
9918
10250
  const isPath = $path1.tagName === 'path';
9919
10251
  const separator = isPath ? ' ' : ',';
9920
- const previousPoints = $path1[morphPointsSymbol];
10252
+ const previousPoints = prevTween ? prevTween._value : null;
9921
10253
  if (previousPoints) $path1.setAttribute(isPath ? 'd' : 'points', previousPoints);
9922
10254
 
9923
10255
  let v1 = '', v2 = '';
@@ -9939,8 +10271,6 @@
9939
10271
  }
9940
10272
  }
9941
10273
 
9942
- $path1[morphPointsSymbol] = v2;
9943
-
9944
10274
  return [v1, v2];
9945
10275
  };
9946
10276
 
@@ -10126,7 +10456,7 @@
10126
10456
  */
10127
10457
  class TextSplitter {
10128
10458
  /**
10129
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10459
+ * @param {Element|NodeList|String|Array<Element>} target
10130
10460
  * @param {TextSplitterParams} [parameters]
10131
10461
  */
10132
10462
  constructor(target, parameters = {}) {
@@ -10198,11 +10528,11 @@
10198
10528
  }
10199
10529
 
10200
10530
  /**
10201
- * @param {(...args: any[]) => Tickable | (() => void)} effect
10531
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
10202
10532
  * @return this
10203
10533
  */
10204
10534
  addEffect(effect) {
10205
- if (!isFnc(effect)) return console.warn('Effect must return a function.');
10535
+ if (!isFnc(effect)) { console.warn('Effect must return a function.'); return this; }
10206
10536
  const refreshableEffect = keepTime(effect);
10207
10537
  this.effects.push(refreshableEffect);
10208
10538
  if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
@@ -10408,7 +10738,7 @@
10408
10738
  }
10409
10739
 
10410
10740
  /**
10411
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
10741
+ * @param {Element|NodeList|String|Array<Element>} target
10412
10742
  * @param {TextSplitterParams} [parameters]
10413
10743
  * @return {TextSplitter}
10414
10744
  */
@@ -10426,9 +10756,261 @@
10426
10756
  return new TextSplitter(target, parameters);
10427
10757
  };
10428
10758
 
10759
+
10760
+
10761
+ /**
10762
+ * '-' 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-')
10763
+ * @param {String} str
10764
+ * @return {String}
10765
+ */
10766
+ const expandCharRanges = (str) => {
10767
+ let result = '';
10768
+ for (let i = 0, l = str.length; i < l; i++) {
10769
+ if (i + 2 < l && str[i + 1] === '-' && str.charCodeAt(i) < str.charCodeAt(i + 2)) {
10770
+ const start = str.charCodeAt(i);
10771
+ const end = str.charCodeAt(i + 2);
10772
+ for (let c = start; c <= end; c++) result += String.fromCharCode(c);
10773
+ i += 2;
10774
+ } else {
10775
+ result += str[i];
10776
+ }
10777
+ }
10778
+ return result;
10779
+ };
10780
+
10781
+ const charSets = {
10782
+ lowercase: 'a-z',
10783
+ uppercase: 'A-Z',
10784
+ numbers: '0-9',
10785
+ symbols: '!%#_|*+=',
10786
+ braille: '⠀-⣿',
10787
+ blocks: '▀-▟',
10788
+ shades: '░-▓',
10789
+ };
10790
+
10791
+ const originalTexts = new WeakMap();
10792
+
10793
+ /**
10794
+ * Returns a function-based tween value that scrambles the target's text content,
10795
+ * progressively revealing the original text.
10796
+ *
10797
+ * @param {ScrambleTextParams} [params]
10798
+ * @return {FunctionValue}
10799
+ */
10800
+ const scrambleText = (params = {}) => {
10801
+ if (!params) params = {};
10802
+ const charsParam = params.chars;
10803
+ const easeFn = parseEase(params.ease || 'linear');
10804
+ const text = params.text;
10805
+ const fromParam = params.from;
10806
+ const reversed = params.reversed || false;
10807
+ const perturbation = params.perturbation || 0;
10808
+ const cursorParam = params.cursor;
10809
+ const cursorChars = cursorParam === true ? '_'
10810
+ : typeof cursorParam === 'number' ? String.fromCharCode(cursorParam)
10811
+ : typeof cursorParam === 'string' ? cursorParam
10812
+ : '';
10813
+ const cursorLen = cursorChars.length;
10814
+ const seed = params.seed || 0;
10815
+ const override = params.override !== undefined ? params.override : true;
10816
+ const revealRate = params.revealRate || 60;
10817
+ const interval = 1000 * globals.timeScale / revealRate;
10818
+ const settleDuration = params.settleDuration || 300 * globals.timeScale;
10819
+ const settleRate = params.settleRate || 30;
10820
+ const durationParam = params.duration;
10821
+ const revealDelayParam = params.revealDelay;
10822
+ const delayParam = params.delay;
10823
+ const onChange = params.onChange || noop;
10824
+
10825
+ return (target, index, targets, prevTween) => {
10826
+ const rawChars = typeof charsParam === 'function' ? charsParam(target, index, targets) : (charsParam || 'a-zA-Z0-9!%#_');
10827
+ const characters = expandCharRanges(charSets[rawChars] || rawChars);
10828
+ const totalChars = characters.length - 1;
10829
+ const duration = typeof durationParam === 'function' ? durationParam(target, index, targets) : durationParam;
10830
+ const revealDelay = typeof revealDelayParam === 'function' ? revealDelayParam(target, index, targets) : (revealDelayParam || 0);
10831
+ const delay = typeof delayParam === 'function' ? delayParam(target, index, targets) : (delayParam || 0);
10832
+ const rng = seed ? createSeededRandom(seed) : createSeededRandom();
10833
+ if (!originalTexts.has(target)) originalTexts.set(target, target.textContent);
10834
+ const startingText = prevTween ? prevTween._value : target.textContent;
10835
+ const targetText = text !== undefined
10836
+ ? (typeof text === 'function' ? text(target, index, targets) : text)
10837
+ : prevTween ? prevTween._value
10838
+ : originalTexts.get(target);
10839
+ const settledText = targetText === ' ' || targetText === '&nbsp;' ? ' ' : targetText;
10840
+ const startLength = startingText === ' ' ? 0 : startingText.length;
10841
+ const endLength = settledText.length;
10842
+ const overrideChars = override === true ? characters
10843
+ : typeof override === 'string' && override.length > 0 ? expandCharRanges(charSets[/** @type {String} */(override)] || /** @type {String} */(override))
10844
+ : null;
10845
+ const totalOverrideChars = overrideChars ? overrideChars.length - 1 : 0;
10846
+ // Space override uses &nbsp; so the browser doesn't collapse consecutive spaces in innerHTML
10847
+ const overrideChar = override === ' ' ? ' ' : null;
10848
+ // When starting from blank, only animate the target text length to avoid padding beyond it
10849
+ const animLength = override === '' ? endLength : Math.max(startLength, endLength);
10850
+ // Compute total duration from interval spacing and settle time, or use the explicit duration
10851
+ const animDuration = duration > 0 ? duration : (animLength - 1) * interval + settleDuration;
10852
+ const computedDuration = round$1((animDuration + revealDelay) / globals.timeScale, 0) * globals.timeScale;
10853
+ const revealDelayRatio = revealDelay > 0 ? round$1(revealDelay / computedDuration, 12) : 0;
10854
+ // Auto-resolve reveal direction: shrinking text reveals from right, growing from left
10855
+ const resolvedFrom = fromParam === undefined || fromParam === 'auto' ? (endLength < startLength ? 'right' : 'left') : fromParam;
10856
+ const charOrder = new Int32Array(animLength);
10857
+ if (resolvedFrom === 'random') {
10858
+ for (let i = 0; i < animLength; i++) charOrder[i] = i;
10859
+ for (let i = animLength - 1; i > 0; i--) {
10860
+ const j = rng(0, i);
10861
+ const t = charOrder[i]; charOrder[i] = charOrder[j]; charOrder[j] = t;
10862
+ }
10863
+ } else {
10864
+ const ref = resolvedFrom === 'right' ? (override === '' || !startLength ? animLength : startLength) - 1
10865
+ : resolvedFrom === 'center' ? ((override === '' || !startLength ? animLength : startLength) - 1) / 2
10866
+ : typeof resolvedFrom === 'number' ? resolvedFrom
10867
+ : 0;
10868
+ const abs = Math.abs;
10869
+ const indices = new Array(animLength);
10870
+ for (let i = 0; i < animLength; i++) indices[i] = i;
10871
+ indices.sort((a, b) => abs(a - ref) - abs(b - ref));
10872
+ for (let i = 0; i < animLength; i++) charOrder[indices[i]] = i;
10873
+ }
10874
+ if (reversed) {
10875
+ const last = animLength - 1;
10876
+ for (let i = 0; i < animLength; i++) charOrder[i] = last - charOrder[i];
10877
+ }
10878
+ // settleRatio is the fraction of the animation each character spends in the active scrambling zone
10879
+ const settleRatio = round$1(settleDuration / animDuration, 12);
10880
+ // settleSpacing is the time gap between consecutive characters entering the active zone
10881
+ const settleSpacing = round$1((1 - settleRatio) / animLength, 12);
10882
+ const cursorZone = cursorLen * settleSpacing;
10883
+ // stepRatio controls how often scramble characters refresh (based on settleRate)
10884
+ const stepRatio = round$1(1000 * globals.timeScale / (settleRate * computedDuration), 12);
10885
+ // Pre-compute per-character start and settle times
10886
+ const charStarts = new Float32Array(animLength);
10887
+ const charEnds = new Float32Array(animLength);
10888
+ const scale = perturbation > 0 ? perturbation * settleRatio : 0;
10889
+ for (let c = 0; c < animLength; c++) {
10890
+ const so = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
10891
+ const eo = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
10892
+ charStarts[c] = charOrder[c] * settleSpacing + so;
10893
+ charEnds[c] = Math.ceil((charStarts[c] + settleRatio + eo) / stepRatio) * stepRatio;
10894
+ }
10895
+ // When text shrinks with non-sequential from modes, delay target settle times past all extras
10896
+ if (endLength < animLength && resolvedFrom !== 'left' && resolvedFrom !== 'right' && resolvedFrom !== 'random') {
10897
+ let maxExtraEnd = 0;
10898
+ for (let c = endLength; c < animLength; c++) {
10899
+ if (charEnds[c] > maxExtraEnd) maxExtraEnd = charEnds[c];
10900
+ }
10901
+ const targets = new Array(endLength);
10902
+ for (let c = 0; c < endLength; c++) targets[c] = c;
10903
+ targets.sort((a, b) => charOrder[a] - charOrder[b]);
10904
+ const targetSpacing = (1 - maxExtraEnd) / endLength;
10905
+ for (let i = 0; i < endLength; i++) {
10906
+ const revealTime = maxExtraEnd + i * targetSpacing;
10907
+ if (revealTime > charEnds[targets[i]]) {
10908
+ charEnds[targets[i]] = revealTime;
10909
+ }
10910
+ }
10911
+ }
10912
+ // charCache holds the current scramble character for each position, refreshed at settleRate
10913
+ const charCache = new Array(animLength);
10914
+ for (let c = 0; c < animLength; c++) {
10915
+ charCache[c] = characters[rng(0, totalChars)];
10916
+ }
10917
+ // overrideCache holds scramble characters for the starting text (override: true or custom string)
10918
+ const overrideCache = overrideChars ? (overrideChars === characters ? charCache : new Array(animLength)) : null;
10919
+ if (overrideCache && overrideCache !== charCache) {
10920
+ for (let c = 0; c < animLength; c++) {
10921
+ overrideCache[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, overrideChars.length - 1)];
10922
+ }
10923
+ }
10924
+ // Build the initial display text based on override mode
10925
+ let fillStartText = startingText;
10926
+ if (!prevTween) {
10927
+ if (override === '') {
10928
+ fillStartText = '';
10929
+ } else if (overrideChars) {
10930
+ fillStartText = '';
10931
+ for (let c = 0; c < startLength; c++) {
10932
+ fillStartText += startingText[c] === ' ' ? ' ' : /** @type {Array<String>} */(overrideCache)[c];
10933
+ }
10934
+ }
10935
+ }
10936
+
10937
+ let lastValue = -1;
10938
+ let lastStep = -1;
10939
+ let scrambled = '';
10940
+ const hasOverride = override !== '';
10941
+ const hasOverrideChars = !!overrideChars;
10942
+ const hasCursor = cursorLen > 0;
10943
+
10944
+ return {
10945
+ from: 0,
10946
+ to: 1,
10947
+ duration: computedDuration,
10948
+ delay: delay,
10949
+ ease: 'linear',
10950
+ modifier: (v) => {
10951
+ if (v === lastValue) return scrambled;
10952
+ lastValue = v;
10953
+ if (delay > 0 && v <= 0) { scrambled = startingText; return startingText; }
10954
+ if (v <= 0) { scrambled = fillStartText; return fillStartText; }
10955
+ if (v >= 1) { scrambled = settledText; return settledText; }
10956
+ scrambled = '';
10957
+ // Only refresh scramble characters when we cross a settleRate step boundary
10958
+ const currentStep = (v / stepRatio) | 0;
10959
+ const refreshChars = currentStep !== lastStep;
10960
+ if (refreshChars) lastStep = currentStep;
10961
+ // Subtract delay ratio to get the effective animation progress
10962
+ const linear = revealDelayRatio > 0 ? (v - revealDelayRatio) / (1 - revealDelayRatio) : v;
10963
+ const t = linear > 0 ? easeFn(linear) : 0;
10964
+ for (let c = 0; c < animLength; c++) {
10965
+ // Each character has its own start/end window based on its reveal order
10966
+ const charStart = charStarts[c];
10967
+ const charEnd = charEnds[c];
10968
+ // Settled zone: character has finished its transition
10969
+ if (t >= charEnd) {
10970
+ if (c < endLength) scrambled += settledText[c];
10971
+ continue;
10972
+ }
10973
+ // Pre-transition zone: reveal wave hasn't reached this character yet
10974
+ if (t <= 0 || t < charStart) {
10975
+ if (hasOverride && c < startLength) {
10976
+ if (hasOverrideChars) {
10977
+ if (startingText[c] === ' ') {
10978
+ scrambled += ' ';
10979
+ } else {
10980
+ if (refreshChars) /** @type {Array<String>} */(overrideCache)[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, totalOverrideChars)];
10981
+ scrambled += /** @type {Array<String>} */(overrideCache)[c];
10982
+ }
10983
+ } else {
10984
+ // Default (override: false): show the original starting text
10985
+ scrambled += startingText[c];
10986
+ }
10987
+ }
10988
+ continue;
10989
+ }
10990
+ // Active zone: character is between charStart and charEnd
10991
+ const isSpace = (c < endLength && settledText[c] === ' ') || (c < startLength && startingText[c] === ' ');
10992
+ if (isSpace) {
10993
+ scrambled += ' ';
10994
+ } else if (hasCursor && t - charStart < cursorZone) {
10995
+ // Cursor sub-zone: show cursor character based on position within cursor width
10996
+ scrambled += cursorChars[cursorLen - 1 - (((t - charStart) / settleSpacing) | 0)];
10997
+ } else {
10998
+ // Scramble zone: show cycling random characters
10999
+ if (refreshChars) charCache[c] = characters[rng(0, totalChars)];
11000
+ scrambled += charCache[c];
11001
+ }
11002
+ }
11003
+ if (refreshChars) onChange(scrambled, t);
11004
+ return scrambled;
11005
+ }
11006
+ }
11007
+ }
11008
+ };
11009
+
10429
11010
  var index = /*#__PURE__*/Object.freeze({
10430
11011
  __proto__: null,
10431
11012
  TextSplitter: TextSplitter,
11013
+ scrambleText: scrambleText,
10432
11014
  split: split,
10433
11015
  splitText: splitText
10434
11016
  });
@@ -10445,6 +11027,7 @@
10445
11027
  exports.Timeline = Timeline;
10446
11028
  exports.Timer = Timer;
10447
11029
  exports.WAAPIAnimation = WAAPIAnimation;
11030
+ exports.addChild = addChild;
10448
11031
  exports.animate = animate;
10449
11032
  exports.clamp = clamp;
10450
11033
  exports.cleanInlineStyles = cleanInlineStyles;
@@ -10464,7 +11047,9 @@
10464
11047
  exports.eases = eases;
10465
11048
  exports.easings = index$3;
10466
11049
  exports.engine = engine;
11050
+ exports.forEachChildren = forEachChildren;
10467
11051
  exports.get = get;
11052
+ exports.globals = globals;
10468
11053
  exports.irregular = irregular;
10469
11054
  exports.keepTime = keepTime;
10470
11055
  exports.lerp = lerp;
@@ -10478,8 +11063,10 @@
10478
11063
  exports.random = random;
10479
11064
  exports.randomPick = randomPick;
10480
11065
  exports.remove = remove;
11066
+ exports.removeChild = removeChild;
10481
11067
  exports.round = round;
10482
11068
  exports.roundPad = roundPad;
11069
+ exports.scrambleText = scrambleText;
10483
11070
  exports.scrollContainers = scrollContainers;
10484
11071
  exports.set = set;
10485
11072
  exports.shuffle = shuffle;