animejs 4.4.1 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +2 -3
  2. package/dist/bundles/anime.esm.js +462 -213
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +462 -213
  5. package/dist/bundles/anime.umd.min.js +2 -2
  6. package/dist/modules/adapters/index.cjs +14 -0
  7. package/dist/modules/adapters/index.d.ts +1 -0
  8. package/dist/modules/adapters/index.js +8 -0
  9. package/dist/modules/adapters/registry.cjs +149 -0
  10. package/dist/modules/adapters/registry.d.ts +65 -0
  11. package/dist/modules/adapters/registry.js +146 -0
  12. package/dist/modules/adapters/three/adapter.cjs +26 -0
  13. package/dist/modules/adapters/three/adapter.d.ts +15 -0
  14. package/dist/modules/adapters/three/adapter.js +24 -0
  15. package/dist/modules/adapters/three/helpers.cjs +297 -0
  16. package/dist/modules/adapters/three/helpers.d.ts +89 -0
  17. package/dist/modules/adapters/three/helpers.js +280 -0
  18. package/dist/modules/adapters/three/index.cjs +20 -0
  19. package/dist/modules/adapters/three/index.d.ts +2 -0
  20. package/dist/modules/adapters/three/index.js +12 -0
  21. package/dist/modules/adapters/three/instance.cjs +368 -0
  22. package/dist/modules/adapters/three/instance.d.ts +133 -0
  23. package/dist/modules/adapters/three/instance.js +365 -0
  24. package/dist/modules/adapters/three/object3d.cjs +214 -0
  25. package/dist/modules/adapters/three/object3d.d.ts +1 -0
  26. package/dist/modules/adapters/three/object3d.js +212 -0
  27. package/dist/modules/adapters/three/resolvers.cjs +105 -0
  28. package/dist/modules/adapters/three/resolvers.d.ts +1 -0
  29. package/dist/modules/adapters/three/resolvers.js +103 -0
  30. package/dist/modules/adapters/three/uniform.cjs +41 -0
  31. package/dist/modules/adapters/three/uniform.d.ts +1 -0
  32. package/dist/modules/adapters/three/uniform.js +39 -0
  33. package/dist/modules/animatable/animatable.cjs +2 -1
  34. package/dist/modules/animatable/animatable.d.ts +2 -1
  35. package/dist/modules/animatable/animatable.js +2 -1
  36. package/dist/modules/animatable/index.cjs +1 -1
  37. package/dist/modules/animatable/index.js +1 -1
  38. package/dist/modules/animation/additive.cjs +1 -1
  39. package/dist/modules/animation/additive.js +1 -1
  40. package/dist/modules/animation/animation.cjs +43 -16
  41. package/dist/modules/animation/animation.d.ts +5 -0
  42. package/dist/modules/animation/animation.js +45 -18
  43. package/dist/modules/animation/composition.cjs +38 -35
  44. package/dist/modules/animation/composition.js +38 -35
  45. package/dist/modules/animation/index.cjs +1 -1
  46. package/dist/modules/animation/index.js +1 -1
  47. package/dist/modules/core/clock.cjs +11 -15
  48. package/dist/modules/core/clock.d.ts +0 -2
  49. package/dist/modules/core/clock.js +11 -15
  50. package/dist/modules/core/colors.cjs +1 -1
  51. package/dist/modules/core/colors.js +1 -1
  52. package/dist/modules/core/consts.cjs +15 -1
  53. package/dist/modules/core/consts.d.ts +2 -0
  54. package/dist/modules/core/consts.js +14 -2
  55. package/dist/modules/core/globals.cjs +7 -4
  56. package/dist/modules/core/globals.d.ts +8 -2
  57. package/dist/modules/core/globals.js +8 -5
  58. package/dist/modules/core/helpers.cjs +2 -2
  59. package/dist/modules/core/helpers.js +3 -3
  60. package/dist/modules/core/render.cjs +64 -14
  61. package/dist/modules/core/render.js +65 -15
  62. package/dist/modules/core/styles.cjs +16 -2
  63. package/dist/modules/core/styles.js +16 -2
  64. package/dist/modules/core/targets.cjs +11 -13
  65. package/dist/modules/core/targets.js +11 -13
  66. package/dist/modules/core/transforms.cjs +1 -1
  67. package/dist/modules/core/transforms.js +1 -1
  68. package/dist/modules/core/units.cjs +1 -1
  69. package/dist/modules/core/units.js +1 -1
  70. package/dist/modules/core/values.cjs +73 -82
  71. package/dist/modules/core/values.d.ts +1 -2
  72. package/dist/modules/core/values.js +76 -84
  73. package/dist/modules/draggable/draggable.cjs +1 -1
  74. package/dist/modules/draggable/draggable.js +1 -1
  75. package/dist/modules/draggable/index.cjs +1 -1
  76. package/dist/modules/draggable/index.js +1 -1
  77. package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
  78. package/dist/modules/easings/cubic-bezier/index.js +1 -1
  79. package/dist/modules/easings/eases/index.cjs +1 -1
  80. package/dist/modules/easings/eases/index.js +1 -1
  81. package/dist/modules/easings/eases/parser.cjs +3 -3
  82. package/dist/modules/easings/eases/parser.d.ts +4 -5
  83. package/dist/modules/easings/eases/parser.js +3 -3
  84. package/dist/modules/easings/index.cjs +1 -1
  85. package/dist/modules/easings/index.js +1 -1
  86. package/dist/modules/easings/irregular/index.cjs +1 -1
  87. package/dist/modules/easings/irregular/index.js +1 -1
  88. package/dist/modules/easings/linear/index.cjs +1 -1
  89. package/dist/modules/easings/linear/index.js +1 -1
  90. package/dist/modules/easings/none.cjs +1 -1
  91. package/dist/modules/easings/none.js +1 -1
  92. package/dist/modules/easings/spring/index.cjs +1 -1
  93. package/dist/modules/easings/spring/index.js +1 -1
  94. package/dist/modules/easings/steps/index.cjs +1 -1
  95. package/dist/modules/easings/steps/index.js +1 -1
  96. package/dist/modules/engine/engine.cjs +4 -2
  97. package/dist/modules/engine/engine.js +4 -2
  98. package/dist/modules/engine/index.cjs +1 -1
  99. package/dist/modules/engine/index.js +1 -1
  100. package/dist/modules/events/index.cjs +1 -1
  101. package/dist/modules/events/index.js +1 -1
  102. package/dist/modules/events/scroll.cjs +3 -1
  103. package/dist/modules/events/scroll.js +3 -1
  104. package/dist/modules/index.cjs +1 -1
  105. package/dist/modules/index.js +1 -1
  106. package/dist/modules/layout/index.cjs +1 -1
  107. package/dist/modules/layout/index.js +1 -1
  108. package/dist/modules/layout/layout.cjs +1 -1
  109. package/dist/modules/layout/layout.js +1 -1
  110. package/dist/modules/scope/index.cjs +1 -1
  111. package/dist/modules/scope/index.js +1 -1
  112. package/dist/modules/scope/scope.cjs +1 -1
  113. package/dist/modules/scope/scope.js +1 -1
  114. package/dist/modules/svg/drawable.cjs +1 -1
  115. package/dist/modules/svg/drawable.js +1 -1
  116. package/dist/modules/svg/helpers.cjs +1 -1
  117. package/dist/modules/svg/helpers.js +1 -1
  118. package/dist/modules/svg/index.cjs +1 -1
  119. package/dist/modules/svg/index.js +1 -1
  120. package/dist/modules/svg/morphto.cjs +1 -1
  121. package/dist/modules/svg/morphto.js +1 -1
  122. package/dist/modules/svg/motionpath.cjs +1 -1
  123. package/dist/modules/svg/motionpath.js +1 -1
  124. package/dist/modules/text/index.cjs +1 -1
  125. package/dist/modules/text/index.js +1 -1
  126. package/dist/modules/text/scramble.cjs +12 -2
  127. package/dist/modules/text/scramble.d.ts +9 -1
  128. package/dist/modules/text/scramble.js +12 -2
  129. package/dist/modules/text/split.cjs +2 -1
  130. package/dist/modules/text/split.js +2 -1
  131. package/dist/modules/timeline/index.cjs +1 -1
  132. package/dist/modules/timeline/index.js +1 -1
  133. package/dist/modules/timeline/position.cjs +1 -1
  134. package/dist/modules/timeline/position.js +1 -1
  135. package/dist/modules/timeline/timeline.cjs +14 -5
  136. package/dist/modules/timeline/timeline.d.ts +3 -3
  137. package/dist/modules/timeline/timeline.js +14 -5
  138. package/dist/modules/timer/index.cjs +1 -1
  139. package/dist/modules/timer/index.js +1 -1
  140. package/dist/modules/timer/timer.cjs +1 -1
  141. package/dist/modules/timer/timer.js +1 -1
  142. package/dist/modules/types/index.d.ts +36 -11
  143. package/dist/modules/utils/chainable.cjs +1 -1
  144. package/dist/modules/utils/chainable.js +1 -1
  145. package/dist/modules/utils/index.cjs +1 -1
  146. package/dist/modules/utils/index.js +1 -1
  147. package/dist/modules/utils/number.cjs +1 -1
  148. package/dist/modules/utils/number.js +1 -1
  149. package/dist/modules/utils/random.cjs +4 -3
  150. package/dist/modules/utils/random.d.ts +1 -1
  151. package/dist/modules/utils/random.js +4 -3
  152. package/dist/modules/utils/stagger.cjs +67 -13
  153. package/dist/modules/utils/stagger.js +69 -15
  154. package/dist/modules/utils/target.cjs +4 -1
  155. package/dist/modules/utils/target.js +4 -1
  156. package/dist/modules/utils/time.cjs +6 -5
  157. package/dist/modules/utils/time.d.ts +1 -1
  158. package/dist/modules/utils/time.js +6 -5
  159. package/dist/modules/waapi/composition.cjs +1 -1
  160. package/dist/modules/waapi/composition.js +1 -1
  161. package/dist/modules/waapi/index.cjs +1 -1
  162. package/dist/modules/waapi/index.js +1 -1
  163. package/dist/modules/waapi/waapi.cjs +1 -1
  164. package/dist/modules/waapi/waapi.js +1 -1
  165. package/package.json +38 -5
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - ESM bundle
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -39,6 +39,12 @@
39
39
  /** @typedef {Timer&JSAnimation&Timeline} CallbackArgument */
40
40
  /** @typedef {Animatable|Tickable|WAAPIAnimation|Draggable|ScrollObserver|TextSplitter|Scope|AutoLayout} Revertible */
41
41
 
42
+ /**
43
+ * @typedef {Object} TweakRegister
44
+ * @property {String} type
45
+ * @property {*} defaultValue
46
+ */
47
+
42
48
  // Stagger types
43
49
 
44
50
  /**
@@ -58,11 +64,18 @@
58
64
  * @property {Number|'first'|'center'|'last'|'random'|Array.<Number>} [from]
59
65
  * @property {Boolean} [reversed]
60
66
  * @property {Array.<Number>|Boolean} [grid]
61
- * @property {('x'|'y')} [axis]
62
- * @property {String|((target: Target, i: Number, length: Number) => Number)} [use]
67
+ * @property {('x'|'y'|'z')} [axis]
68
+ * @property {String | { method(target: Target, i: Number, length: Number): Number }['method']} [use]
63
69
  * @property {Number} [total]
64
70
  * @property {EasingParam} [ease]
65
71
  * @property {TweenModifier} [modifier]
72
+ * @property {Number|[Number, Number]} [jitter] Additive uniform noise on the
73
+ * computed stagger value. Number form gives flat `+/-jitter`; tuple form
74
+ * ramps the magnitude `start -> end` across the from/axis/grid ordering
75
+ * and respects `ease`.
76
+ * @property {Boolean|Number} [seed] Seed for jitter draws and `from: 'random'`
77
+ * shuffling. `false` (default) uses Math.random. `true` seeds with `0`. A
78
+ * number is used directly as the seed.
66
79
  */
67
80
 
68
81
  // Targets types
@@ -138,10 +151,7 @@
138
151
 
139
152
  /**
140
153
  * @template T
141
- * @callback Callback
142
- * @param {T} self - Returns itself
143
- * @param {PointerEvent} [e]
144
- * @return {*}
154
+ * @typedef {{ method(self: T): * }['method']} Callback
145
155
  */
146
156
 
147
157
  /**
@@ -185,12 +195,17 @@
185
195
  // Tween types
186
196
 
187
197
  /**
198
+ * @typedef {Number|String|TweenKeyValue|EasingParam|Array.<Number|String|TweenKeyValue>} FunctionValueReturn
199
+ */
200
+
201
+ /**
202
+ * @template [T=FunctionValueReturn]
188
203
  * @callback FunctionValue
189
204
  * @param {Target} [target] - The animated target
190
205
  * @param {Number} [index] - The target index
191
206
  * @param {TargetsArray} [targets] - The array of all animated targets
192
207
  * @param {Tween|null} [prevTween] - The previous sibling tween for the same target and property
193
- * @return {Number|String|TweenObjectValue|EasingParam|Array.<Number|String|TweenObjectValue>}
208
+ * @return {T}
194
209
  */
195
210
 
196
211
  /**
@@ -207,7 +222,7 @@
207
222
  * @property {JSAnimation} parent
208
223
  * @property {String} property
209
224
  * @property {Target} target
210
- * @property {String|Number} _value
225
+ * @property {String|Number|Object} _value
211
226
  * @property {Function|null} _toFunc
212
227
  * @property {Function|null} _fromFunc
213
228
  * @property {EasingFunction} _ease
@@ -226,7 +241,11 @@
226
241
  * @property {Number} _startTime
227
242
  * @property {Number} _changeDuration
228
243
  * @property {Number} _absoluteStartTime
244
+ * @property {Number} _absoluteUpdateStartTime
245
+ * @property {Number} _absoluteEndTime
246
+ * @property {Number} _hasFromValue
229
247
  * @property {tweenTypes} _tweenType
248
+ * @property {((target: any, value: number, tween: Tween) => void) | null} _setter
230
249
  * @property {valueTypes} _valueType
231
250
  * @property {Number} _composition
232
251
  * @property {Number} _isOverlapped
@@ -247,8 +266,8 @@
247
266
  * @property {Number} n - Single number value
248
267
  * @property {String} u - Value unit
249
268
  * @property {String} o - Value operator
250
- * @property {Array.<Number>} d - Array of Numbers (in case of complex value type)
251
- * @property {Array.<String>} s - Strings (in case of complex value type)
269
+ * @property {Array.<Number>} d - Array of Numbers (complex / color value type)
270
+ * @property {Array.<String>} s - Strings (complex value type)
252
271
  */
253
272
 
254
273
  /** @typedef {{_head: null|Tween, _tail: null|Tween}} TweenPropertySiblings */
@@ -259,7 +278,7 @@
259
278
  // JSAnimation types
260
279
 
261
280
  /**
262
- * @typedef {Number|String|FunctionValue|EasingParam} TweenParamValue
281
+ * @typedef {Number|String|FunctionValue|EasingParam|TweakRegister} TweenParamValue
263
282
  */
264
283
 
265
284
  /**
@@ -452,7 +471,7 @@
452
471
  */
453
472
 
454
473
  /**
455
- * @typedef {Record<String, TweenParamValue | EasingParam | TweenModifier | TweenComposition | AnimatablePropertyParamsOptions> & AnimatablePropertyParamsOptions} AnimatableParams
474
+ * @typedef {Record<String, TweenParamValue | EasingParam | TweenModifier | TweenComposition | AnimatablePropertyParamsOptions | Callback<JSAnimation>> & AnimatablePropertyParamsOptions & TickableCallbacks<JSAnimation> & RenderableCallbacks<JSAnimation>} AnimatableParams
456
475
  */
457
476
 
458
477
  // Scope types
@@ -495,7 +514,7 @@
495
514
  /**
496
515
  * @callback ScopeMethod
497
516
  * @param {...*} args
498
- * @return {ScopeCleanupCallback|void}
517
+ * @return {*}
499
518
  */
500
519
 
501
520
  // Scroll types
@@ -725,6 +744,11 @@ const maxFps = 240;
725
744
  const emptyString = '';
726
745
  const cssVarPrefix = 'var(';
727
746
 
747
+ // Arrays
748
+
749
+ // Shared sentinel for tween slots that don't hold array data. Never mutated, only read; COMPLEX and COLOR tweens always replace the slot before writing.
750
+ const emptyArray = [];
751
+
728
752
  const shortTransforms = /*#__PURE__*/ (() => {
729
753
  const map = new Map();
730
754
  map.set('x', 'translateX');
@@ -758,6 +782,13 @@ const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) =>
758
782
  /** @return {void} */
759
783
  const noop = () => {};
760
784
 
785
+ /**
786
+ * @template T
787
+ * @param {T} v
788
+ * @return {T}
789
+ */
790
+ const noopModifier = v => v;
791
+
761
792
  // Regex
762
793
 
763
794
  const validRgbHslRgx = /\)\s*[-.\d]/;
@@ -779,10 +810,13 @@ const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/;
779
810
  /**
780
811
  * @typedef {Object} EditorGlobals
781
812
  * @property {boolean} showPanel
782
- * @property {boolean} synced
783
813
  * @property {Function} addAnimation
814
+ * @property {Function} addSet
784
815
  * @property {Function} addTimeline
785
816
  * @property {Function} addTimelineChild
817
+ * @property {Function} addTimelineLabel
818
+ * @property {Function} addTimelineCall
819
+ * @property {Function} addTimelineSync
786
820
  * @property {Function} resolveStagger
787
821
  * @property {Object|null} _head
788
822
  * @property {Object|null} _tail
@@ -805,7 +839,7 @@ const defaults = {
805
839
  loopDelay: 0,
806
840
  ease: 'out(2)',
807
841
  composition: compositionTypes.replace,
808
- modifier: v => v,
842
+ modifier: noopModifier,
809
843
  onBegin: noop,
810
844
  onBeforeUpdate: noop,
811
845
  onUpdate: noop,
@@ -835,7 +869,7 @@ const globals = {
835
869
  editor: null,
836
870
  };
837
871
 
838
- const globalVersions = { version: '4.4.1', engine: null };
872
+ const globalVersions = { version: '4.5.0', engine: null };
839
873
 
840
874
  if (isBrowser) {
841
875
  if (!win.AnimeJS) win.AnimeJS = [];
@@ -982,7 +1016,7 @@ const snap$1 = (v, increment) => isArr(increment) ? increment.reduce((closest, c
982
1016
  * @param {Number} factor - Interpolation factor in the range [0, 1]
983
1017
  * @return {Number} The interpolated value
984
1018
  */
985
- const lerp$1 = (start, end, factor) => start + (end - start) * factor;
1019
+ const lerp$1 = (start, end, factor) => factor === 1 ? end : factor === 0 ? start : start + (end - start) * factor;
986
1020
 
987
1021
  /**
988
1022
  * Replaces infinity with maximum safe value
@@ -1231,6 +1265,61 @@ const buildTransformString = (props) => {
1231
1265
  return str;
1232
1266
  };
1233
1267
 
1268
+ /**
1269
+ * Anime.js adapter API. Each library or class group that wants to extend `animate()` and `utils.set()` calls `registerAdapter()` to create its own `Adapter`. The returned `Adapter` exposes `registerTargetAdapter(detect)` for per-class detection and `registerPropertyResolver(fn)` for global Color / Vector / pattern-based fallbacks.
1270
+ *
1271
+ * import { registerAdapter } from 'animejs/adapters';
1272
+ *
1273
+ * const myAdapter = registerAdapter();
1274
+ * const widget = myAdapter.registerTargetAdapter((t) => t instanceof MyWidget);
1275
+ * widget.registerProperty('value',
1276
+ * (t) => t.getValue(),
1277
+ * (target, value) => target.setValue(value),
1278
+ * );
1279
+ *
1280
+ * For scalar tweens, `value` is the interpolated number. For color and complex tweens it is `undefined`; read `tween._numbers` instead. `gate(target)` scopes the prop to a subset of matching targets.
1281
+ *
1282
+ * Resolution order: every Adapter's target adapters in registration order (first match wins) then every Adapter's property resolvers (first non-null wins) then engine direct property path.
1283
+ */
1284
+
1285
+
1286
+ const adapters = /** @type {Adapter[]} */([]);
1287
+
1288
+ /**
1289
+ * Internal resolution. Tries every Adapter's target adapters first (in registration order, first match wins), then every Adapter's property resolvers.
1290
+ *
1291
+ * @param {any} target
1292
+ * @param {string} name
1293
+ * @return {TargetAdapterEntry | null}
1294
+ */
1295
+ function resolveAdapterEntry(target, name) {
1296
+ if (!target) return null;
1297
+ const al = adapters.length;
1298
+ outer: for (let i = 0; i < al; i++) {
1299
+ const a = adapters[i];
1300
+ if (a.detect && !a.detect(target)) continue;
1301
+ const tas = a.targetAdapters;
1302
+ for (let j = 0, m = tas.length; j < m; j++) {
1303
+ const ta = tas[j];
1304
+ if (ta.detect(target)) {
1305
+ const entry = ta.props[name];
1306
+ if (entry && (!entry.gate || entry.gate(target))) return entry;
1307
+ break outer;
1308
+ }
1309
+ }
1310
+ }
1311
+ for (let i = 0; i < al; i++) {
1312
+ const a = adapters[i];
1313
+ if (a.detect && !a.detect(target)) continue;
1314
+ const rs = a.propertyResolvers;
1315
+ for (let j = 0, m = rs.length; j < m; j++) {
1316
+ const entry = rs[j](target, name);
1317
+ if (entry) return entry;
1318
+ }
1319
+ }
1320
+ return null;
1321
+ }
1322
+
1234
1323
 
1235
1324
 
1236
1325
  /**
@@ -1328,6 +1417,21 @@ const setValue = (targetValue, defaultValue) => {
1328
1417
  return isUnd(targetValue) ? defaultValue : targetValue;
1329
1418
  };
1330
1419
 
1420
+ /**
1421
+ * Resolve against the target when it's a DOM element, otherwise fall back to :root so non-DOM targets like three.js meshes and custom adapters still pick up CSS variables defined on the document.
1422
+ *
1423
+ * @param {String} value
1424
+ * @param {Target} target
1425
+ * @return {String|Number}
1426
+ */
1427
+ const resolveCssVar = (value, target) => {
1428
+ const match = value.match(cssVariableMatchRgx);
1429
+ const el = target[isDomSymbol] ? target : document.documentElement;
1430
+ let computed = getComputedStyle(/** @type {HTMLElement} */(el))?.getPropertyValue(match[1]);
1431
+ if ((!computed || computed.trim() === emptyString) && match[2]) computed = match[2].trim();
1432
+ return computed || 0;
1433
+ };
1434
+
1331
1435
  /**
1332
1436
  * @param {TweenPropValue} value
1333
1437
  * @param {Target} target
@@ -1338,30 +1442,26 @@ const setValue = (targetValue, defaultValue) => {
1338
1442
  * @return {any}
1339
1443
  */
1340
1444
  const getFunctionValue = (value, target, index, targets, store, prevTween) => {
1341
- let func;
1342
1445
  if (isFnc(value)) {
1343
- func = () => {
1446
+ if (!store) {
1447
+ const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
1448
+ // Fallback to 0 if the function returns undefined, NaN, null, false or 0
1449
+ return !isNaN(+computed) ? +computed : computed || 0;
1450
+ }
1451
+ const func = () => {
1344
1452
  const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
1345
- // Fallback to 0 if the function returns undefined / NaN / null / false / 0
1346
1453
  return !isNaN(+computed) ? +computed : computed || 0;
1347
1454
  };
1348
- } else if (isStr(value) && stringStartsWith(value, cssVarPrefix)) {
1349
- func = () => {
1350
- const match = value.match(cssVariableMatchRgx);
1351
- const cssVarName = match[1];
1352
- const fallbackValue = match[2];
1353
- let computed = getComputedStyle(/** @type {HTMLElement} */(target))?.getPropertyValue(cssVarName);
1354
- // Use fallback if CSS variable is not set or empty
1355
- if ((!computed || computed.trim() === emptyString) && fallbackValue) {
1356
- computed = fallbackValue.trim();
1357
- }
1358
- return computed || 0;
1359
- };
1360
- } else {
1361
- return value;
1455
+ store.func = func;
1456
+ return func();
1362
1457
  }
1363
- if (store) store.func = func;
1364
- return func();
1458
+ if (isStr(value) && stringStartsWith(value, cssVarPrefix)) {
1459
+ if (!store) return resolveCssVar(/** @type {String} */(value), target);
1460
+ const func = () => resolveCssVar(/** @type {String} */(value), target);
1461
+ store.func = func;
1462
+ return func();
1463
+ }
1464
+ return value;
1365
1465
  };
1366
1466
 
1367
1467
  /**
@@ -1408,6 +1508,12 @@ const getCSSValue = (target, propName, animationInlineStyles) => {
1408
1508
  */
1409
1509
  const getOriginalAnimatableValue = (target, propName, tweenType, animationInlineStyles) => {
1410
1510
  const type = !isUnd(tweenType) ? tweenType : getTweenType(target, propName);
1511
+ const adapterProp = resolveAdapterEntry(target, propName);
1512
+ if (adapterProp) {
1513
+ const value = adapterProp.get(target);
1514
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
1515
+ return value == null ? 0 : value;
1516
+ }
1411
1517
  if (type === tweenTypes.OBJECT) {
1412
1518
  const value = target[propName];
1413
1519
  if (value && animationInlineStyles) animationInlineStyles[propName] = value;
@@ -1449,7 +1555,7 @@ const createDecomposedValueTargetObject = () => {
1449
1555
  };
1450
1556
 
1451
1557
  /**
1452
- * @param {String|Number} rawValue
1558
+ * @param {String|Number|Object} rawValue
1453
1559
  * @param {TweenDecomposedValue} targetObject
1454
1560
  * @return {TweenDecomposedValue}
1455
1561
  */
@@ -1467,39 +1573,38 @@ const decomposeRawValue = (rawValue, targetObject) => {
1467
1573
  // It's a number
1468
1574
  targetObject.n = num;
1469
1575
  return targetObject;
1576
+ }
1577
+ // let str = /** @type {String} */(rawValue).trim();
1578
+ let str = /** @type {String} */(rawValue);
1579
+ // Parsing operators (+=, -=, *=) manually is much faster than using regex here
1580
+ if (str[1] === '=') {
1581
+ targetObject.o = str[0];
1582
+ str = str.slice(2);
1583
+ }
1584
+ // Skip exec regex if the value type is complex or color to avoid long regex backtracking
1585
+ const unitMatch = str.includes(' ') ? false : unitsExecRgx.exec(str);
1586
+ if (unitMatch) {
1587
+ // Has a number and a unit
1588
+ targetObject.t = valueTypes.UNIT;
1589
+ targetObject.n = +unitMatch[1];
1590
+ targetObject.u = unitMatch[2];
1591
+ return targetObject;
1592
+ } else if (targetObject.o) {
1593
+ // Has an operator (+=, -=, *=)
1594
+ targetObject.n = +str;
1595
+ return targetObject;
1596
+ } else if (isCol(str)) {
1597
+ // Color string
1598
+ targetObject.t = valueTypes.COLOR;
1599
+ targetObject.d = convertColorStringValuesToRgbaArray(str);
1600
+ return targetObject;
1470
1601
  } else {
1471
- // let str = /** @type {String} */(rawValue).trim();
1472
- let str = /** @type {String} */(rawValue);
1473
- // Parsing operators (+=, -=, *=) manually is much faster than using regex here
1474
- if (str[1] === '=') {
1475
- targetObject.o = str[0];
1476
- str = str.slice(2);
1477
- }
1478
- // Skip exec regex if the value type is complex or color to avoid long regex backtracking
1479
- const unitMatch = str.includes(' ') ? false : unitsExecRgx.exec(str);
1480
- if (unitMatch) {
1481
- // Has a number and a unit
1482
- targetObject.t = valueTypes.UNIT;
1483
- targetObject.n = +unitMatch[1];
1484
- targetObject.u = unitMatch[2];
1485
- return targetObject;
1486
- } else if (targetObject.o) {
1487
- // Has an operator (+=, -=, *=)
1488
- targetObject.n = +str;
1489
- return targetObject;
1490
- } else if (isCol(str)) {
1491
- // Is a color
1492
- targetObject.t = valueTypes.COLOR;
1493
- targetObject.d = convertColorStringValuesToRgbaArray(str);
1494
- return targetObject;
1495
- } else {
1496
- // Is a more complex string (generally svg coords, calc() or filters CSS values)
1497
- const matchedNumbers = str.match(digitWithExponentRgx);
1498
- targetObject.t = valueTypes.COMPLEX;
1499
- targetObject.d = matchedNumbers ? matchedNumbers.map(Number) : [];
1500
- targetObject.s = str.split(digitWithExponentRgx) || [];
1501
- return targetObject;
1502
- }
1602
+ // Is a more complex string (generally svg coords, calc() or filters CSS values)
1603
+ const matchedNumbers = str.match(digitWithExponentRgx);
1604
+ targetObject.t = valueTypes.COMPLEX;
1605
+ targetObject.d = matchedNumbers ? matchedNumbers.map(Number) : [];
1606
+ targetObject.s = str.split(digitWithExponentRgx) || [];
1607
+ return targetObject;
1503
1608
  }
1504
1609
  };
1505
1610
 
@@ -1520,30 +1625,6 @@ const decomposeTweenValue = (tween, targetObject) => {
1520
1625
 
1521
1626
  const decomposedOriginalValue = createDecomposedValueTargetObject();
1522
1627
 
1523
- /**
1524
- * @param {Tween} tween
1525
- * @param {Number} progress
1526
- * @param {Number} precision
1527
- * @return {String}
1528
- */
1529
- const composeColorValue = (tween, progress, precision) => {
1530
- const mod = tween._modifier;
1531
- const fn = tween._fromNumbers;
1532
- const tn = tween._toNumbers;
1533
- const r = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[0], tn[0], progress))), 0, 255), 0);
1534
- const g = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[1], tn[1], progress))), 0, 255), 0);
1535
- const b = round$1(clamp$1(/** @type {Number} */(mod(lerp$1(fn[2], tn[2], progress))), 0, 255), 0);
1536
- const a = clamp$1(/** @type {Number} */(mod(round$1(lerp$1(fn[3], tn[3], progress), precision))), 0, 1);
1537
- if (tween._composition !== compositionTypes.none) {
1538
- const ns = tween._numbers;
1539
- ns[0] = r;
1540
- ns[1] = g;
1541
- ns[2] = b;
1542
- ns[3] = a;
1543
- }
1544
- return `rgba(${r},${g},${b},${a})`;
1545
- };
1546
-
1547
1628
  /**
1548
1629
  * @param {Tween} tween
1549
1630
  * @param {Number} progress
@@ -1555,15 +1636,14 @@ const composeComplexValue = (tween, progress, precision) => {
1555
1636
  const fn = tween._fromNumbers;
1556
1637
  const tn = tween._toNumbers;
1557
1638
  const ts = tween._strings;
1558
- const hasComposition = tween._composition !== compositionTypes.none;
1559
1639
  let v = ts[0];
1560
1640
  for (let j = 0, l = tn.length; j < l; j++) {
1561
1641
  const n = /** @type {Number} */(mod(round$1(lerp$1(fn[j], tn[j], progress), precision)));
1562
1642
  const s = ts[j + 1];
1563
1643
  v += `${s ? n + s : n}`;
1564
- if (hasComposition) {
1565
- tween._numbers[j] = n;
1566
- }
1644
+ // Keep _numbers fresh for every tween, not only composed ones, so a non-composition setter that reads the lerped triplet such as three transformOrigin still animates.
1645
+ // Potential optimization, skip the write when nothing reads it: if (hasComposition || tween._setter) tween._numbers[j] = n;
1646
+ tween._numbers[j] = n;
1567
1647
  }
1568
1648
  return v;
1569
1649
  };
@@ -1615,12 +1695,14 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1615
1695
  // Execute the "expensive" iterations calculations only when necessary
1616
1696
  if (iterationCount > 1) {
1617
1697
  // bitwise NOT operator seems to be generally faster than Math.floor() across browsers
1618
- const currentIteration = ~~(tickableCurrentTime / (iterationDuration + (isCurrentTimeEqualOrAboveDuration ? 0 : _loopDelay)));
1698
+ const period = iterationDuration + (isCurrentTimeEqualOrAboveDuration ? 0 : _loopDelay);
1699
+ const currentIteration = ~~(tickableCurrentTime / period);
1619
1700
  tickable._currentIteration = clamp$1(currentIteration, 0, iterationCount);
1620
1701
  // Prevent the iteration count to go above the max iterations when reaching the end of the animation
1621
1702
  if (isCurrentTimeEqualOrAboveDuration) tickable._currentIteration--;
1622
1703
  isOdd = tickable._currentIteration % 2;
1623
- iterationElapsedTime = tickableCurrentTime % (iterationDuration + _loopDelay) || 0;
1704
+ // Derive elapsed from the same `~~` truncation that gave currentIteration. Using `% period` here can disagree with `~~(/period)` under float drift at iteration boundaries and write the wrong end of the tween for one frame.
1705
+ iterationElapsedTime = tickableCurrentTime - currentIteration * period || 0;
1624
1706
  }
1625
1707
 
1626
1708
  // Checks if exactly one of _reversed and (_alternate && isOdd) is true
@@ -1652,12 +1734,14 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1652
1734
  if (
1653
1735
  forcedTick ||
1654
1736
  tickMode === tickModes.AUTO && (
1655
- time >= tickableDelay && time <= tickableEndTime || // Normal render
1737
+ // Timeline children render from their offset instead of their delay so the gap left by a truncated sibling is covered on seek.
1738
+ time >= (parent && tickableDelay > 0 ? 0 : tickableDelay) && time <= tickableEndTime || // Normal render
1656
1739
  time <= tickableDelay && tickablePrevTime > tickableDelay || // Playhead is before the animation start time so make sure the animation is at its initial state
1657
1740
  time >= tickableEndTime && tickablePrevTime !== duration // Playhead is after the animation end time so make sure the animation is at its end state
1658
1741
  ) ||
1659
1742
  iterationTime >= tickableEndTime && tickablePrevTime !== duration ||
1660
- iterationTime <= tickableDelay && tickablePrevTime > 0 ||
1743
+ // iterationTime is per-iteration, compared to the delay to catch a backward seek into a looped iteration's delay region. Exclude the final settled end, where iterationTime clamps to duration and would falsely match the delay region when the delay exceeds the duration.
1744
+ iterationTime <= tickableDelay && tickablePrevTime > 0 && !isCurrentTimeEqualOrAboveDuration ||
1661
1745
  time <= tickablePrevTime && tickablePrevTime === duration && completed || // Force a render if a seek occurs on an completed animation
1662
1746
  isCurrentTimeEqualOrAboveDuration && !completed && isSetter // This prevents 0 duration tickables to be skipped
1663
1747
  ) {
@@ -1673,7 +1757,8 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1673
1757
 
1674
1758
  // Time has jumped more than globals.tickThreshold so consider this tick manual
1675
1759
  const forcedRender = forcedTick || (isRunningBackwards ? deltaTime * -1 : deltaTime) >= globals.tickThreshold;
1676
- const absoluteTime = tickable._offset + (parent ? parent._offset : 0) + tickableDelay + iterationTime;
1760
+ // Round to match the precision of tween._absoluteStartTime so equal-time boundary checks compare cleanly without floating point drift from the unrounded _offset.
1761
+ const absoluteTime = round$1(tickable._offset + (parent ? parent._offset : 0) + tickableDelay + iterationTime, 12);
1677
1762
 
1678
1763
  // Only Animation can have tweens, Timer returns undefined
1679
1764
  let tween = /** @type {Tween} */(/** @type {JSAnimation} */(tickable)._head);
@@ -1692,15 +1777,38 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1692
1777
  const tweenNextRep = tween._nextRep;
1693
1778
  const tweenPrevRep = tween._prevRep;
1694
1779
  const tweenHasComposition = tweenComposition !== compositionTypes.none;
1780
+ // The previous sibling stops writing at its truncated end, so this tween takes over the hold from that point.
1781
+ const tweenPrevRepEndTime = tweenPrevRep ? tweenPrevRep._absoluteStartTime + tweenPrevRep._changeDuration : 0;
1782
+ const tweenPrevRepIsCrossParent = tweenPrevRep && tweenPrevRep.parent !== tween.parent;
1783
+ // Same parent keyframes take over at their own start, end plus delay equals the next start by construction.
1784
+ // Cross parent siblings take over at their update start.
1785
+ // Negative delay siblings take over at their own start instead.
1786
+ const tweenNextRepTakeover = !tweenNextRep || tweenNextRep._isOverridden ? tweenAbsEndTime :
1787
+ tweenNextRep.parent === tween.parent ? tweenAbsEndTime + tweenNextRep._delay :
1788
+ tweenNextRep._absoluteStartTime < tweenNextRep._absoluteUpdateStartTime ? tweenNextRep._absoluteStartTime : tweenNextRep._absoluteUpdateStartTime;
1695
1789
 
1696
1790
  if ((forcedRender || (
1697
- (tweenCurrentTime !== tweenChangeDuration || absoluteTime <= tweenAbsEndTime + (tweenNextRep ? tweenNextRep._delay : 0)) &&
1698
- (tweenCurrentTime !== 0 || absoluteTime >= tween._absoluteStartTime)
1699
- )) && (!tweenHasComposition || (
1791
+ // Tail keyframes always re-evaluate the gate so an earlier keyframe cannot leave the target stale by writing past its own range after a backward seek.
1792
+ (tweenCurrentTime !== tweenChangeDuration || absoluteTime <= tweenNextRepTakeover ||
1793
+ (tweenPrevRep && !tweenPrevRepIsCrossParent && (!tweenNextRep || tweenNextRep.parent !== tween.parent))) &&
1794
+ // A cross parent tween re-renders its from value from the previous sibling truncated end so the handoff gap holds.
1795
+ // A keyframe re-renders its from revert while the next keyframe time is stale so a backward jump over its range cannot leave the next value in place.
1796
+ (tweenCurrentTime !== 0 || absoluteTime >= tween._absoluteStartTime ||
1797
+ (tweenPrevRepIsCrossParent && !tween._hasFromValue && !tweenPrevRep._isOverridden && absoluteTime >= tweenPrevRepEndTime) ||
1798
+ (tweenNextRep && !tweenNextRep._isOverridden && tweenNextRep.parent === tween.parent && tweenNextRep._currentTime !== 0 && iterationTime < tweenNextRep._startTime))
1799
+ )) &&
1800
+ // Non-first keyframes wait until the iteration reaches their own start before rendering, so the previous keyframe can handle the from-revert when scrubbed backward past this tween's range.
1801
+ (!tweenPrevRep || tweenPrevRepIsCrossParent || iterationTime >= tween._startTime) &&
1802
+ (!tweenHasComposition || (
1700
1803
  !tween._isOverridden &&
1701
1804
  (!tween._isOverlapped || absoluteTime <= tweenAbsEndTime) &&
1702
- (!tweenNextRep || (tweenNextRep._isOverridden || absoluteTime <= tweenNextRep._absoluteStartTime)) &&
1703
- (!tweenPrevRep || (tweenPrevRep._isOverridden || (absoluteTime >= (tweenPrevRep._absoluteStartTime + tweenPrevRep._changeDuration) + tween._delay)))
1805
+ // The next sibling owns the value past its takeover point, so yielding there keeps writes single owner in both directions.
1806
+ (!tweenNextRep || tweenNextRep._isOverridden || absoluteTime <= tweenNextRepTakeover) &&
1807
+ // The previous sibling owns the value up to its truncated end.
1808
+ // Cross parent tweens take over the hold from that point, explicit from values wait for their own start.
1809
+ (!tweenPrevRep || (tweenPrevRep._isOverridden || (!tweenPrevRepIsCrossParent ?
1810
+ absoluteTime >= tweenPrevRepEndTime + tween._delay :
1811
+ absoluteTime >= tween._absoluteStartTime || (!tween._hasFromValue && absoluteTime >= tweenPrevRepEndTime))))
1704
1812
  ))
1705
1813
  ) {
1706
1814
 
@@ -1711,7 +1819,7 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1711
1819
  const tweenType = tween._tweenType;
1712
1820
  const tweenIsObject = tweenType === tweenTypes.OBJECT;
1713
1821
  const tweenIsNumber = tweenValueType === valueTypes.NUMBER;
1714
- // Only round the in-between frames values if the final value is a string
1822
+ // Only round the in-between frames values if the final value is a string. Object targets consume raw numbers, so rounding is dead work there.
1715
1823
  const tweenPrecision = (tweenIsNumber && tweenIsObject) || tweenProgress === 0 || tweenProgress === 1 ? -1 : globals.precision;
1716
1824
 
1717
1825
  // Recompose tween value
@@ -1727,7 +1835,22 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1727
1835
  number = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
1728
1836
  value = `${number}${tween._unit}`;
1729
1837
  } else if (tweenValueType === valueTypes.COLOR) {
1730
- value = composeColorValue(tween, tweenProgress, tweenPrecision);
1838
+ const ns = tween._numbers;
1839
+ const fn = tween._fromNumbers;
1840
+ const tn = tween._toNumbers;
1841
+ const omt = 1 - tweenProgress;
1842
+ const fr = fn[0], fg = fn[1], fb = fn[2];
1843
+ const tr = tn[0], tg = tn[1], tb = tn[2];
1844
+ // RGB channels lerp in pseudo-linear space (square inputs, sqrt result) to approximate gamma-correct blending.
1845
+ // See https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear.
1846
+ ns[0] = /** @type {Number} */(tweenModifier(Math.sqrt(fr * fr * omt + tr * tr * tweenProgress)));
1847
+ ns[1] = /** @type {Number} */(tweenModifier(Math.sqrt(fg * fg * omt + tg * tg * tweenProgress)));
1848
+ ns[2] = /** @type {Number} */(tweenModifier(Math.sqrt(fb * fb * omt + tb * tb * tweenProgress)));
1849
+ ns[3] = /** @type {Number} */(tweenModifier(lerp$1(fn[3], tn[3], tweenProgress)));
1850
+ // The rgba string is built only for the dispatch path or the internalRender composition tick (setters handles the color comp)
1851
+ if (!tween._setter || internalRender) {
1852
+ value = `rgba(${round$1(ns[0], 0)},${round$1(ns[1], 0)},${round$1(ns[2], 0)},${ns[3]})`;
1853
+ }
1731
1854
  } else if (tweenValueType === valueTypes.COMPLEX) {
1732
1855
  value = composeComplexValue(tween, tweenProgress, tweenPrecision);
1733
1856
  }
@@ -1742,7 +1865,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1742
1865
  const tweenProperty = tween.property;
1743
1866
  tweenTarget = tween.target;
1744
1867
 
1745
- if (tweenIsObject) {
1868
+ if (tween._setter) {
1869
+ tween._setter(tweenTarget, number, tween);
1870
+ } else if (tweenIsObject) {
1746
1871
  tweenTarget[tweenProperty] = value;
1747
1872
  } else if (tweenType === tweenTypes.ATTRIBUTE) {
1748
1873
  /** @type {DOMTarget} */(tweenTarget).setAttribute(tweenProperty, /** @type {String} */(value));
@@ -1770,6 +1895,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1770
1895
  tween._value = value;
1771
1896
  }
1772
1897
 
1898
+ } else if (tweenCurrentTime && tweenPrevRep && !tweenPrevRepIsCrossParent && iterationTime < tween._startTime) {
1899
+ // Mark the keyframe as reverted when the playhead moves before its start, the previous keyframe owns the from revert and writes it once.
1900
+ tween._currentTime = 0;
1773
1901
  }
1774
1902
 
1775
1903
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
@@ -1877,6 +2005,8 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
1877
2005
 
1878
2006
  forEachChildren(tl, (/** @type {JSAnimation} */child) => {
1879
2007
  const childTime = round$1((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
2008
+ // Skip past-end siblings on backward iteration so their progress=1 to-values don't render last and overwrite the active sibling's write. Compare against _delay + duration so children with a normalized delay are not skipped while still inside their active range.
2009
+ if (tlIsRunningBackwards && childTime > child._delay + child.duration) return;
1880
2010
  const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
1881
2011
  tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
1882
2012
  if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
@@ -1953,7 +2083,20 @@ const revertValues = (renderable, inlineStylesOnly = false) => {
1953
2083
  const tweenType = tween._tweenType;
1954
2084
  const originalInlinedValue = tween._inlineValue;
1955
2085
  const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
1956
- if (tweenType === tweenTypes.OBJECT) {
2086
+ if (tween._setter) {
2087
+ if (!inlineStylesOnly && !tweenHadNoInlineValue) {
2088
+ // Re-seed the original value to the _number / _numbers props so the setter can write the original state instead of re-applying the current frame.
2089
+ decomposeRawValue(originalInlinedValue, decomposedOriginalValue);
2090
+ if (decomposedOriginalValue.d) {
2091
+ const src = decomposedOriginalValue.d;
2092
+ const dst = tween._numbers;
2093
+ for (let i = 0, l = src.length; i < l; i++) dst[i] = src[i];
2094
+ } else {
2095
+ tween._number = decomposedOriginalValue.n;
2096
+ }
2097
+ tween._setter(tween.target, tween._number, tween);
2098
+ }
2099
+ } else if (tweenType === tweenTypes.OBJECT) {
1957
2100
  if (!inlineStylesOnly && !tweenHadNoInlineValue) {
1958
2101
  tweenTarget[tweenProperty] = originalInlinedValue;
1959
2102
  }
@@ -2030,8 +2173,6 @@ class Clock {
2030
2173
  /** @type {Number} */
2031
2174
  this._lastTime = initTime;
2032
2175
  /** @type {Number} */
2033
- this._scheduledTime = 0;
2034
- /** @type {Number} */
2035
2176
  this._frameDuration = K / maxFps;
2036
2177
  /** @type {Number} */
2037
2178
  this._fps = maxFps;
@@ -2050,14 +2191,12 @@ class Clock {
2050
2191
  }
2051
2192
 
2052
2193
  set fps(frameRate) {
2053
- const previousFrameDuration = this._frameDuration;
2054
2194
  const fr = +frameRate;
2055
2195
  const fps = fr < minValue ? minValue : fr;
2056
2196
  const frameDuration = K / fps;
2057
2197
  if (fps > defaults.frameRate) defaults.frameRate = fps;
2058
2198
  this._fps = fps;
2059
2199
  this._frameDuration = frameDuration;
2060
- this._scheduledTime += frameDuration - previousFrameDuration;
2061
2200
  }
2062
2201
 
2063
2202
  get speed() {
@@ -2074,17 +2213,17 @@ class Clock {
2074
2213
  * @return {tickModes}
2075
2214
  */
2076
2215
  requestTick(time) {
2077
- const scheduledTime = this._scheduledTime;
2078
- this._lastTickTime = time;
2079
- // If the current time is lower than the scheduled time
2080
- // this means not enough time has passed to hit one frameDuration
2081
- // so skip that frame
2082
- if (time < scheduledTime) return tickModes.NONE;
2083
2216
  const frameDuration = this._frameDuration;
2084
- const frameDelta = time - scheduledTime;
2085
- // Ensures that _scheduledTime progresses in steps of at least 1 frameDuration.
2086
- // Skips ahead if the actual elapsed time is higher.
2087
- this._scheduledTime += frameDelta < frameDuration ? frameDuration : frameDelta;
2217
+ const elapsed = time - this._lastTickTime;
2218
+ const scaled = frameDuration * .25;
2219
+ const tolerance = scaled < 4 ? scaled : 4;
2220
+ // Tolerance prevents dropping frames that arrive a bit early due to RAF jitter
2221
+ // typically <= ~25% of frame duration and capped at 4ms so it doesn't dominate at high fps.
2222
+ // e.g. at 60fps (frameDuration=16.667ms) a frame arriving after 15ms:
2223
+ // - without tolerance: 15 < 16.667 -> skip
2224
+ // - with tolerance: 15 + 4 >= 16.667 -> tick
2225
+ if (elapsed + tolerance < frameDuration) return tickModes.NONE;
2226
+ this._lastTickTime = elapsed >= frameDuration ? time - (elapsed % frameDuration) : time;
2088
2227
  return tickModes.AUTO;
2089
2228
  }
2090
2229
 
@@ -2244,7 +2383,9 @@ class Engine extends Clock {
2244
2383
  }
2245
2384
 
2246
2385
  set speed(playbackRate) {
2247
- this._speed = playbackRate * globals.timeScale;
2386
+ const speed = playbackRate * globals.timeScale;
2387
+ if (this._speed === speed) return;
2388
+ this._speed = speed;
2248
2389
  forEachChildren(this, (/** @type {Tickable} */child) => child.speed = child._speed);
2249
2390
  }
2250
2391
 
@@ -2377,7 +2518,7 @@ const composeTween = (tween, siblings) => {
2377
2518
  if (prevSibling) {
2378
2519
 
2379
2520
  const prevParent = prevSibling.parent;
2380
- const prevAbsEndTime = prevSibling._absoluteStartTime + prevSibling._changeDuration;
2521
+ const prevAbsEndTime = prevSibling._absoluteEndTime;
2381
2522
 
2382
2523
  // Handle looped animations tween
2383
2524
 
@@ -2403,7 +2544,8 @@ const composeTween = (tween, siblings) => {
2403
2544
 
2404
2545
  }
2405
2546
 
2406
- const absoluteUpdateStartTime = tweenAbsStartTime - tween._delay;
2547
+ // Read the precision-matched update-start instead of subtracting tween._delay live so sequential keyframes touching at a boundary don't trigger a phantom overlap from float drift.
2548
+ const absoluteUpdateStartTime = tween._absoluteUpdateStartTime;
2407
2549
 
2408
2550
  if (prevAbsEndTime > absoluteUpdateStartTime) {
2409
2551
 
@@ -2423,37 +2565,41 @@ const composeTween = (tween, siblings) => {
2423
2565
  }
2424
2566
  }
2425
2567
 
2426
- // Pause (and cancel) the parent if it only contains overlapped tweens
2568
+ // Skip the cancel cascade when both tweens share the same parent timeline, a timeline cannot replace itself.
2569
+ const tweenParentTL = tween.parent.parent;
2570
+ if (!tweenParentTL || tweenParentTL !== prevParent.parent) {
2427
2571
 
2428
- let pausePrevParentAnimation = true;
2572
+ let pausePrevParentAnimation = true;
2429
2573
 
2430
- forEachChildren(prevParent, (/** @type Tween */t) => {
2431
- if (!t._isOverlapped) pausePrevParentAnimation = false;
2432
- });
2574
+ forEachChildren(prevParent, (/** @type Tween */t) => {
2575
+ if (!t._isOverlapped) pausePrevParentAnimation = false;
2576
+ });
2433
2577
 
2434
- if (pausePrevParentAnimation) {
2435
- const prevParentTL = prevParent.parent;
2436
- if (prevParentTL) {
2437
- let pausePrevParentTL = true;
2438
- forEachChildren(prevParentTL, (/** @type JSAnimation */a) => {
2439
- if (a !== prevParent) {
2440
- forEachChildren(a, (/** @type Tween */t) => {
2441
- if (!t._isOverlapped) pausePrevParentTL = false;
2442
- });
2578
+ if (pausePrevParentAnimation) {
2579
+ const prevParentTL = prevParent.parent;
2580
+ if (prevParentTL) {
2581
+ let pausePrevParentTL = true;
2582
+ forEachChildren(prevParentTL, (/** @type JSAnimation */a) => {
2583
+ if (a !== prevParent) {
2584
+ forEachChildren(a, (/** @type Tween */t) => {
2585
+ if (!t._isOverlapped) pausePrevParentTL = false;
2586
+ });
2587
+ }
2588
+ });
2589
+ if (pausePrevParentTL) {
2590
+ prevParentTL.cancel();
2443
2591
  }
2444
- });
2445
- if (pausePrevParentTL) {
2446
- prevParentTL.cancel();
2592
+ } else {
2593
+ prevParent.cancel();
2594
+ // Previously, calling .cancel() on a timeline child would affect the render order of other children
2595
+ // Worked around this by marking it as .completed and using .pause() for safe removal in the engine loop
2596
+ // This is no longer needed since timeline tween composition is now handled separately
2597
+ // Keeping this here for reference
2598
+ // prevParent.completed = true;
2599
+ // prevParent.pause();
2447
2600
  }
2448
- } else {
2449
- prevParent.cancel();
2450
- // Previously, calling .cancel() on a timeline child would affect the render order of other children
2451
- // Worked around this by marking it as .completed and using .pause() for safe removal in the engine loop
2452
- // This is no longer needed since timeline tween composition is now handled separately
2453
- // Keeping this here for reference
2454
- // prevParent.completed = true;
2455
- // prevParent.pause();
2456
2601
  }
2602
+
2457
2603
  }
2458
2604
 
2459
2605
  }
@@ -2508,14 +2654,12 @@ const composeTween = (tween, siblings) => {
2508
2654
  tween._number = 0;
2509
2655
  lookupTween._fromNumber = toNumber;
2510
2656
 
2511
- if (tween._toNumbers) {
2657
+ if (tween._toNumbers.length) {
2512
2658
  const toNumbers = cloneArray(tween._toNumbers);
2513
- if (toNumbers) {
2514
- toNumbers.forEach((value, i) => {
2515
- tween._fromNumbers[i] = lookupTween._fromNumbers[i] - value;
2516
- tween._toNumbers[i] = 0;
2517
- });
2518
- }
2659
+ toNumbers.forEach((value, i) => {
2660
+ tween._fromNumbers[i] = lookupTween._fromNumbers[i] - value;
2661
+ tween._toNumbers[i] = 0;
2662
+ });
2519
2663
  lookupTween._fromNumbers = toNumbers;
2520
2664
  }
2521
2665
 
@@ -3216,18 +3360,16 @@ function parseTargets(targets) {
3216
3360
  function registerTargets(targets) {
3217
3361
  const parsedTargetsArray = parseTargets(targets);
3218
3362
  const parsedTargetsLength = parsedTargetsArray.length;
3219
- if (parsedTargetsLength) {
3220
- for (let i = 0; i < parsedTargetsLength; i++) {
3221
- const target = parsedTargetsArray[i];
3222
- if (!target[isRegisteredTargetSymbol]) {
3223
- target[isRegisteredTargetSymbol] = true;
3224
- const isSvgType = isSvg(target);
3225
- const isDom = /** @type {DOMTarget} */(target).nodeType || isSvgType;
3226
- if (isDom) {
3227
- target[isDomSymbol] = true;
3228
- target[isSvgSymbol] = isSvgType;
3229
- target[transformsSymbol] = {};
3230
- }
3363
+ for (let i = 0; i < parsedTargetsLength; i++) {
3364
+ const target = parsedTargetsArray[i];
3365
+ if (!target[isRegisteredTargetSymbol]) {
3366
+ target[isRegisteredTargetSymbol] = true;
3367
+ const isSvgType = isSvg(target);
3368
+ const isDom = /** @type {DOMTarget} */(target).nodeType || isSvgType;
3369
+ if (isDom) {
3370
+ target[isDomSymbol] = true;
3371
+ target[isSvgSymbol] = isSvgType;
3372
+ target[transformsSymbol] = {};
3231
3373
  }
3232
3374
  }
3233
3375
  }
@@ -3349,8 +3491,8 @@ const easeInFunctions = {
3349
3491
 
3350
3492
  /**
3351
3493
  * @typedef {Object} EasesFunctions
3352
- * @property {typeof none} linear
3353
- * @property {typeof none} none
3494
+ * @property {EasingFunction} linear
3495
+ * @property {EasingFunction} none
3354
3496
  * @property {PowerEasing} in
3355
3497
  * @property {PowerEasing} out
3356
3498
  * @property {PowerEasing} inOut
@@ -3582,6 +3724,11 @@ class JSAnimation extends Timer {
3582
3724
 
3583
3725
  super(/** @type {TimerParams & AnimationParams} */(parameters), parent, parentPosition);
3584
3726
 
3727
+ /** @type {Tween} */
3728
+ this._head;
3729
+ /** @type {Tween} */
3730
+ this._tail;
3731
+
3585
3732
  ++JSAnimationId;
3586
3733
 
3587
3734
  const parsedTargets = registerTargets(targets);
@@ -3638,6 +3785,7 @@ class JSAnimation extends Timer {
3638
3785
  if (isKey(p)) {
3639
3786
 
3640
3787
  const tweenType = getTweenType(target, p);
3788
+ const adapterProp = resolveAdapterEntry(target, p);
3641
3789
 
3642
3790
  const propName = sanitizePropertyName(p, target, tweenType);
3643
3791
 
@@ -3721,7 +3869,7 @@ class JSAnimation extends Timer {
3721
3869
  } else {
3722
3870
  tweenToValue = computedToValue;
3723
3871
  }
3724
- const tweenFromValue = getFunctionValue(key.from, target, ti, tl, null, prevSiblingTween);
3872
+ const tweenFromValue = getFunctionValue(key.from, target, ti, tl, fromFunctionStore, prevSiblingTween);
3725
3873
  const easeToParse = key.ease || tEasing;
3726
3874
 
3727
3875
  const easeFunctionResult = getFunctionValue(easeToParse, target, ti, tl, null, prevSiblingTween);
@@ -3739,9 +3887,13 @@ class JSAnimation extends Timer {
3739
3887
  const hasToValue = !isUnd(tweenToValue);
3740
3888
  const isFromToArray = isArr(tweenToValue);
3741
3889
  const isFromToValue = isFromToArray || (hasFromvalue && hasToValue);
3890
+ // Capture the update-start in local time, the previous sibling's end for keyframes after the first, zero for the first keyframe. Used to derive a precision-matched _absoluteUpdateStartTime below.
3891
+ const tweenUpdateStartLocal = prevTween ? lastTweenChangeEndTime : 0;
3742
3892
  const tweenStartTime = prevTween ? lastTweenChangeEndTime + tweenDelay : tweenDelay;
3743
3893
  // Rounding is necessary here to minimize floating point errors when working in seconds
3744
3894
  const absoluteStartTime = round$1(absoluteOffsetTime + tweenStartTime, 12);
3895
+ // Match the rounding pattern of prevSibling._absoluteEndTime so the composition overlap check compares cleanly when keyframes touch at a boundary.
3896
+ const absoluteUpdateStartTime = round$1(absoluteOffsetTime + tweenUpdateStartLocal, 12);
3745
3897
 
3746
3898
  // Force a onRender callback if the animation contains at least one from value and autoplay is set to false
3747
3899
  if (!shouldTriggerRender && (hasFromvalue || isFromToArray)) shouldTriggerRender = 1;
@@ -3750,9 +3902,9 @@ class JSAnimation extends Timer {
3750
3902
 
3751
3903
  if (tweenComposition !== compositionTypes.none) {
3752
3904
  let nextSibling = siblings._head;
3753
- // Iterate trough all the next siblings until we find a sibling with an equal or inferior start time
3754
- while (nextSibling && !nextSibling._isOverridden && nextSibling._absoluteStartTime <= absoluteStartTime) {
3755
- prevSibling = nextSibling;
3905
+ // Walk prior siblings up to the new tween, skipping overridden ones so the chain resolves to the latest live value instead of stopping at the first override.
3906
+ while (nextSibling && nextSibling._absoluteStartTime <= absoluteStartTime) {
3907
+ if (!nextSibling._isOverridden) prevSibling = nextSibling;
3756
3908
  nextSibling = nextSibling._nextRep;
3757
3909
  // Overrides all the next siblings if the next sibling starts at the same time of after as the new tween start time
3758
3910
  if (nextSibling && nextSibling._absoluteStartTime >= absoluteStartTime) {
@@ -3806,8 +3958,8 @@ class JSAnimation extends Timer {
3806
3958
  if (prevTween) {
3807
3959
  decomposeTweenValue(prevTween, fromTargetObject);
3808
3960
  } else {
3809
- decomposeRawValue(parent && prevSibling && prevSibling.parent.parent === parent ? prevSibling._value :
3810
3961
  // No need to get and parse the original value if the tween is part of a timeline and has a previous sibling part of the same timeline
3962
+ decomposeRawValue(parent && prevSibling && prevSibling.parent.parent === parent ? prevSibling._value :
3811
3963
  getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore), fromTargetObject);
3812
3964
  }
3813
3965
  }
@@ -3846,8 +3998,7 @@ class JSAnimation extends Timer {
3846
3998
  const colorValue = fromTargetObject.t === valueTypes.COLOR ? fromTargetObject : toTargetObject;
3847
3999
  const notColorValue = fromTargetObject.t === valueTypes.COLOR ? toTargetObject : fromTargetObject;
3848
4000
  notColorValue.t = valueTypes.COLOR;
3849
- notColorValue.s = colorValue.s;
3850
- notColorValue.d = [0, 0, 0, 1];
4001
+ notColorValue.d = colorValue.d.map(() => 0);
3851
4002
  }
3852
4003
  }
3853
4004
 
@@ -3877,6 +4028,16 @@ class JSAnimation extends Timer {
3877
4028
  let inlineValue = inlineStylesStore[propName];
3878
4029
  if (!isNil(inlineValue)) inlineStylesStore[propName] = null;
3879
4030
 
4031
+ // Resolve the adapter setter once so render skips the lookup per frame.
4032
+ const tweenSetter = adapterProp ? adapterProp.set : null;
4033
+
4034
+ // Rounding is necessary here to minimize floating point errors when working in seconds
4035
+ lastTweenChangeEndTime = round$1(tweenStartTime + tweenUpdateDuration, 12);
4036
+
4037
+ const fromD = fromTargetObject.d;
4038
+ const toD = toTargetObject.d;
4039
+ const toS = toTargetObject.s;
4040
+
3880
4041
  /** @type {Tween} */
3881
4042
  const tween = {
3882
4043
  parent: this,
@@ -3887,12 +4048,12 @@ class JSAnimation extends Timer {
3887
4048
  _toFunc: toFunctionStore.func,
3888
4049
  _fromFunc: fromFunctionStore.func,
3889
4050
  _ease: parseEase(tweenEasing),
3890
- _fromNumbers: cloneArray(fromTargetObject.d),
3891
- _toNumbers: cloneArray(toTargetObject.d),
3892
- _strings: cloneArray(toTargetObject.s),
4051
+ _fromNumbers: fromD ? cloneArray(fromD) : emptyArray,
4052
+ _toNumbers: toD ? cloneArray(toD) : emptyArray,
4053
+ _strings: toS ? cloneArray(toS) : emptyArray,
3893
4054
  _fromNumber: fromTargetObject.n,
3894
4055
  _toNumber: toTargetObject.n,
3895
- _numbers: cloneArray(fromTargetObject.d), // For additive tween and animatables
4056
+ _numbers: fromD ? cloneArray(fromD) : emptyArray, // For additive tween and animatables
3896
4057
  _number: fromTargetObject.n, // For additive tween and animatables
3897
4058
  _unit: toTargetObject.u,
3898
4059
  _modifier: tweenModifier,
@@ -3902,8 +4063,12 @@ class JSAnimation extends Timer {
3902
4063
  _updateDuration: tweenUpdateDuration,
3903
4064
  _changeDuration: tweenUpdateDuration,
3904
4065
  _absoluteStartTime: absoluteStartTime,
4066
+ _absoluteUpdateStartTime: absoluteUpdateStartTime,
4067
+ _absoluteEndTime: round$1(absoluteOffsetTime + lastTweenChangeEndTime, 12),
4068
+ _hasFromValue: hasFromvalue || isFromToArray ? 1 : 0,
3905
4069
  // NOTE: Investigate bit packing to stores ENUM / BOOL
3906
4070
  _tweenType: tweenType,
4071
+ _setter: tweenSetter,
3907
4072
  _valueType: toTargetObject.t,
3908
4073
  _composition: tweenComposition,
3909
4074
  _isOverlapped: 0,
@@ -3926,10 +4091,11 @@ class JSAnimation extends Timer {
3926
4091
  const vt = tween._valueType;
3927
4092
  if (vt === valueTypes.COMPLEX) {
3928
4093
  tween._value = composeComplexValue(tween, 1, -1);
3929
- } else if (vt === valueTypes.COLOR) {
3930
- tween._value = composeColorValue(tween, 1, -1);
3931
4094
  } else if (vt === valueTypes.UNIT) {
3932
4095
  tween._value = `${tweenModifier(tween._toNumber)}${tween._unit}`;
4096
+ } else if (vt === valueTypes.COLOR) {
4097
+ const d = toTargetObject.d;
4098
+ tween._value = `rgba(${round$1(d[0], 0)},${round$1(d[1], 0)},${round$1(d[2], 0)},${d[3]})`;
3933
4099
  } else {
3934
4100
  tween._value = tweenModifier(tween._toNumber);
3935
4101
  }
@@ -3937,8 +4103,7 @@ class JSAnimation extends Timer {
3937
4103
  if (isNaN(firstTweenChangeStartTime)) {
3938
4104
  firstTweenChangeStartTime = tween._startTime;
3939
4105
  }
3940
- // Rounding is necessary here to minimize floating point errors when working in seconds
3941
- lastTweenChangeEndTime = round$1(tweenStartTime + tweenUpdateDuration, 12);
4106
+
3942
4107
  prevTween = tween;
3943
4108
  animationAnimationLength++;
3944
4109
 
@@ -4044,8 +4209,11 @@ class JSAnimation extends Timer {
4044
4209
  tween._updateDuration = normalizeTime(tween._updateDuration * timeScale);
4045
4210
  tween._changeDuration = normalizeTime(tween._changeDuration * timeScale);
4046
4211
  tween._currentTime *= timeScale;
4212
+ tween._delay *= timeScale;
4047
4213
  tween._startTime *= timeScale;
4048
4214
  tween._absoluteStartTime *= timeScale;
4215
+ tween._absoluteUpdateStartTime *= timeScale;
4216
+ tween._absoluteEndTime *= timeScale;
4049
4217
  });
4050
4218
  return super.stretch(newDuration);
4051
4219
  }
@@ -4174,8 +4342,6 @@ const parseTimelinePosition = (timeline, timePosition) => {
4174
4342
 
4175
4343
 
4176
4344
 
4177
-
4178
-
4179
4345
  /**
4180
4346
  * @param {Timeline} tl
4181
4347
  * @return {Number}
@@ -4368,13 +4534,21 @@ class Timeline extends Timer {
4368
4534
  if (!isUnd(synced) && !isUnd(/** @type {WAAPIAnimation} */(synced).persist)) {
4369
4535
  /** @type {WAAPIAnimation} */(synced).persist = true;
4370
4536
  }
4371
- return this.add(synced, { currentTime: [0, duration], duration, delay: 0, ease: 'linear', playbackEase: 'linear' }, position);
4537
+ const editor = globals.editor;
4538
+ const childHook = editor && editor.addTimelineChild;
4539
+ if (editor && editor.addTimelineSync) {
4540
+ position = editor.addTimelineSync(synced, position, this.id);
4541
+ editor.addTimelineChild = null; // Suppress the per-child hook for the internal .add, sync already registered.
4542
+ }
4543
+ const result = this.add(synced, { currentTime: [0, duration], duration, delay: 0, ease: 'linear', playbackEase: 'linear' }, position);
4544
+ if (editor) editor.addTimelineChild = childHook;
4545
+ return result;
4372
4546
  }
4373
4547
 
4374
4548
  /**
4375
4549
  * @param {TargetsParam} targets
4376
4550
  * @param {AnimationParams} parameters
4377
- * @param {TimelinePosition} [position]
4551
+ * @param {TimelinePosition|StaggerFunction<Number|String>|TweakRegister} [position]
4378
4552
  * @return {this}
4379
4553
  */
4380
4554
  set(targets, parameters, position) {
@@ -4391,6 +4565,7 @@ class Timeline extends Timer {
4391
4565
  */
4392
4566
  call(callback, position) {
4393
4567
  if (isUnd(callback) || callback && !isFnc(callback)) return this;
4568
+ if (globals.editor && globals.editor.addTimelineCall) position = globals.editor.addTimelineCall(callback, position, this.id);
4394
4569
  return this.add({ duration: 0, delay: 0, onComplete: () => callback(this) }, position);
4395
4570
  }
4396
4571
 
@@ -4402,6 +4577,7 @@ class Timeline extends Timer {
4402
4577
  */
4403
4578
  label(labelName, position) {
4404
4579
  if (isUnd(labelName) || labelName && !isStr(labelName)) return this;
4580
+ if (globals.editor && globals.editor.addTimelineLabel) position = globals.editor.addTimelineLabel(labelName, position, this.id);
4405
4581
  this.labels[labelName] = parseTimelinePosition(this, position);
4406
4582
  return this;
4407
4583
  }
@@ -4510,6 +4686,7 @@ class Animatable {
4510
4686
  const callbacksAnimationParams = { v: 1, autoplay: false };
4511
4687
  const properties = {};
4512
4688
  this.targets = [];
4689
+ /** @type {Record<String, JSAnimation>} */
4513
4690
  this.animations = {};
4514
4691
  /** @type {JSAnimation|null} */
4515
4692
  this.callbacks = null;
@@ -5048,6 +5225,9 @@ function get(targetSelector, propName, unit) {
5048
5225
  */
5049
5226
  const set = (targets, parameters) => {
5050
5227
  if (isUnd(parameters)) return;
5228
+ if (globals.editor && globals.editor.addSet) {
5229
+ return globals.editor.addSet(targets, parameters);
5230
+ }
5051
5231
  parameters.duration = minValue;
5052
5232
  // Do not overrides currently active tweens by default
5053
5233
  parameters.composition = setValue(parameters.composition, compositionTypes.none);
@@ -6284,13 +6464,14 @@ const sync = (callback = noop) => {
6284
6464
  };
6285
6465
 
6286
6466
  /**
6287
- * @param {(...args: any[]) => Tickable | ((...args: any[]) => void) | void} constructor
6288
- * @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
6467
+ * @template {Tickable | ((...args: any[]) => void) | void} T
6468
+ * @param {(...args: any[]) => T} constructor
6469
+ * @return {(...args: any[]) => T extends void ? () => void : T}
6289
6470
  */
6290
6471
  const keepTime = constructor => {
6291
6472
  /** @type {Tickable} */
6292
6473
  let tracked;
6293
- return (...args) => {
6474
+ return /** @type {(...args: any[]) => T extends void ? () => void : T} */(/** @type {*} */((...args) => {
6294
6475
  let currentIteration, currentIterationProgress, reversed, alternate, startTime;
6295
6476
  if (tracked) {
6296
6477
  currentIteration = tracked.currentIteration;
@@ -6308,7 +6489,7 @@ const keepTime = constructor => {
6308
6489
  /** @type {Tickable} */(tracked)._startTime = startTime;
6309
6490
  }
6310
6491
  return cleanup || noop;
6311
- }
6492
+ }));
6312
6493
  };
6313
6494
 
6314
6495
 
@@ -6706,12 +6887,14 @@ class ScrollContainer {
6706
6887
 
6707
6888
  refreshScrollObservers() {
6708
6889
  forEachChildren(this, (/** @type {ScrollObserver} */child) => {
6890
+ if (!child.ready) return;
6709
6891
  if (child._debug) {
6710
6892
  child.removeDebug();
6711
6893
  }
6712
6894
  });
6713
6895
  this.updateBounds();
6714
6896
  forEachChildren(this, (/** @type {ScrollObserver} */child) => {
6897
+ if (!child.ready) return;
6715
6898
  child.refresh();
6716
6899
  child.onResize(child);
6717
6900
  if (child._debug) {
@@ -9809,11 +9992,12 @@ const randomPick = items => items[random(0, items.length - 1)];
9809
9992
  * Adapted from https://bost.ocks.org/mike/shuffle/
9810
9993
  *
9811
9994
  * @param {Array} items - The array to shuffle (will be modified in-place)
9995
+ * @param {RandomNumberGenerator} [rnd] - Optional RNG matching the random() signature (defaults to random)
9812
9996
  * @return {Array} The same array reference, now shuffled
9813
9997
  */
9814
- const shuffle = items => {
9998
+ const shuffle = (items, rnd = random) => {
9815
9999
  let m = items.length, t, i;
9816
- while (m) { i = random(0, --m); t = items[m]; items[m] = items[i]; items[i] = t; }
10000
+ while (m) { i = rnd(0, --m); t = items[m]; items[m] = items[i]; items[i] = t; }
9817
10001
  return items;
9818
10002
  };
9819
10003
 
@@ -9858,6 +10042,7 @@ const stagger = (val, params = {}) => {
9858
10042
  let values = [];
9859
10043
  let maxValue = 0;
9860
10044
  let cachedOffset;
10045
+ let jitterSamples = null;
9861
10046
  const from = params.from;
9862
10047
  const reversed = params.reversed;
9863
10048
  const ease = params.ease;
@@ -9879,27 +10064,42 @@ const stagger = (val, params = {}) => {
9879
10064
  const val2 = isRange ? parseNumber(val[1]) : 0;
9880
10065
  const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
9881
10066
  const start = params.start || 0 + (isRange ? val1 : 0);
10067
+ const seed = params.seed;
10068
+ const hasSeed = !isUnd(seed) && seed !== false;
10069
+ const rng = hasSeed ? createSeededRandom(seed === true ? 0 : /** @type {Number} */(seed)) : random;
10070
+ const jitter = params.jitter;
10071
+ const hasJitter = !isUnd(jitter);
10072
+ const jitterIsArr = isArr(jitter);
10073
+ const jitterStart = jitterIsArr ? /** @type {[Number,Number]} */(jitter)[0] : /** @type {Number} */(jitter) || 0;
10074
+ const jitterEnd = jitterIsArr ? /** @type {[Number,Number]} */(jitter)[1] : /** @type {Number} */(jitter) || 0;
9882
10075
  let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
9883
10076
  return (target, i, t, _, tl) => {
9884
10077
  const [ registeredTarget ] = registerTargets(target);
9885
10078
  const total = isUnd(customTotal) ? t.length : customTotal;
9886
10079
  const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
9887
- const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
10080
+ const customIdx = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
10081
+ // Fall back to the natural index when the resolved value lands outside [0, total) so values[staggerIndex] never reads undefined.
10082
+ const staggerIndex = customIdx >= 0 && customIdx < total ? customIdx : i;
9888
10083
  if (fromCenter) fromIndex = (total - 1) / 2;
9889
10084
  if (fromLast) fromIndex = total - 1;
9890
10085
  if (!values.length) {
9891
10086
  if (autoGrid) {
9892
10087
  let hasPositions = true;
10088
+ let has3D = false;
9893
10089
  let minPosX = Infinity;
9894
10090
  let minPosY = Infinity;
10091
+ let minPosZ = Infinity;
9895
10092
  let maxPosX = -Infinity;
9896
10093
  let maxPosY = -Infinity;
10094
+ let maxPosZ = -Infinity;
9897
10095
  const pxArr = [];
9898
10096
  const pyArr = [];
10097
+ const pzArr = [];
9899
10098
  for (let index = 0; index < total; index++) {
9900
10099
  const el = t[index];
9901
10100
  let px = 0;
9902
10101
  let py = 0;
10102
+ let pz = 0;
9903
10103
  let found = false;
9904
10104
  if (el && isFnc(el.getBoundingClientRect)) {
9905
10105
  const rect = el.getBoundingClientRect();
@@ -9911,6 +10111,10 @@ const stagger = (val, params = {}) => {
9911
10111
  if (obj && isNum(obj.x) && isNum(obj.y)) {
9912
10112
  px = obj.x;
9913
10113
  py = obj.y;
10114
+ if (isNum(obj.z)) {
10115
+ pz = obj.z;
10116
+ has3D = true;
10117
+ }
9914
10118
  found = true;
9915
10119
  }
9916
10120
  }
@@ -9920,42 +10124,52 @@ const stagger = (val, params = {}) => {
9920
10124
  }
9921
10125
  pxArr.push(px);
9922
10126
  pyArr.push(py);
10127
+ pzArr.push(pz);
9923
10128
  if (px < minPosX) minPosX = px;
9924
10129
  if (py < minPosY) minPosY = py;
10130
+ if (pz < minPosZ) minPosZ = pz;
9925
10131
  if (px > maxPosX) maxPosX = px;
9926
10132
  if (py > maxPosY) maxPosY = py;
10133
+ if (pz > maxPosZ) maxPosZ = pz;
9927
10134
  }
9928
10135
  if (hasPositions) {
9929
10136
  let fX = pxArr[0];
9930
10137
  let fY = pyArr[0];
10138
+ let fZ = pzArr[0];
9931
10139
  if (fromArr) {
9932
10140
  fX = minPosX + from[0] * (maxPosX - minPosX);
9933
10141
  fY = minPosY + from[1] * (maxPosY - minPosY);
10142
+ fZ = has3D ? minPosZ + (from.length >= 3 ? from[2] : 0.5) * (maxPosZ - minPosZ) : 0;
9934
10143
  } else if (fromCenter) {
9935
10144
  fX = (minPosX + maxPosX) / 2;
9936
10145
  fY = (minPosY + maxPosY) / 2;
10146
+ fZ = (minPosZ + maxPosZ) / 2;
9937
10147
  } else if (fromLast) {
9938
10148
  fX = pxArr[total - 1];
9939
10149
  fY = pyArr[total - 1];
10150
+ fZ = pzArr[total - 1];
9940
10151
  } else if (isNum(from)) {
9941
10152
  fX = pxArr[from];
9942
10153
  fY = pyArr[from];
10154
+ fZ = pzArr[from];
9943
10155
  }
9944
10156
  for (let index = 0; index < total; index++) {
9945
10157
  const distanceX = fX - pxArr[index];
9946
10158
  const distanceY = fY - pyArr[index];
9947
- let value = sqrt(distanceX * distanceX + distanceY * distanceY);
10159
+ const distanceZ = fZ - pzArr[index];
10160
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY + (has3D ? distanceZ * distanceZ : 0));
9948
10161
  if (axis === 'x') value = -distanceX;
9949
10162
  if (axis === 'y') value = -distanceY;
10163
+ if (axis === 'z') value = -distanceZ;
9950
10164
  values.push(value);
9951
10165
  }
9952
10166
  let minDist = Infinity;
9953
- for (let index = 0, l = values.length; index < l; index++) {
10167
+ for (let index = 0; index < total; index++) {
9954
10168
  const absVal = abs(values[index]);
9955
10169
  if (absVal > 0 && absVal < minDist) minDist = absVal;
9956
10170
  }
9957
10171
  if (minDist > 0 && minDist < Infinity) {
9958
- for (let index = 0, l = values.length; index < l; index++) {
10172
+ for (let index = 0; index < total; index++) {
9959
10173
  values[index] = values[index] / minDist;
9960
10174
  }
9961
10175
  }
@@ -9969,32 +10183,51 @@ const stagger = (val, params = {}) => {
9969
10183
  if (!grid) {
9970
10184
  values.push(abs(fromIndex - index));
9971
10185
  } else {
9972
- let fromX, fromY;
10186
+ const dims = grid.length;
10187
+ const wh = grid[0] * grid[1];
10188
+ let fromX, fromY, fromZ;
9973
10189
  if (fromArr) {
9974
10190
  fromX = from[0] * (grid[0] - 1);
9975
10191
  fromY = from[1] * (grid[1] - 1);
10192
+ fromZ = dims === 3 ? (from.length >= 3 ? from[2] : 0.5) * (grid[2] - 1) : 0;
9976
10193
  } else if (fromCenter) {
9977
10194
  fromX = (grid[0] - 1) / 2;
9978
10195
  fromY = (grid[1] - 1) / 2;
10196
+ fromZ = dims === 3 ? (grid[2] - 1) / 2 : 0;
9979
10197
  } else {
9980
10198
  fromX = fromIndex % grid[0];
9981
- fromY = floor(fromIndex / grid[0]);
10199
+ fromY = floor(fromIndex / grid[0]) % grid[1];
10200
+ fromZ = dims === 3 ? floor(fromIndex / wh) : 0;
9982
10201
  }
9983
10202
  const toX = index % grid[0];
9984
- const toY = floor(index / grid[0]);
10203
+ const toY = floor(index / grid[0]) % grid[1];
10204
+ const toZ = dims === 3 ? floor(index / wh) : 0;
9985
10205
  const distanceX = fromX - toX;
9986
10206
  const distanceY = fromY - toY;
9987
- let value = sqrt(distanceX * distanceX + distanceY * distanceY);
10207
+ const distanceZ = fromZ - toZ;
10208
+ let value = sqrt(distanceX * distanceX + distanceY * distanceY + (dims === 3 ? distanceZ * distanceZ : 0));
9988
10209
  if (axis === 'x') value = -distanceX;
9989
10210
  if (axis === 'y') value = -distanceY;
10211
+ if (axis === 'z') value = -distanceZ;
9990
10212
  values.push(value);
9991
10213
  }
9992
10214
  }
9993
10215
  }
9994
- maxValue = max(...values);
9995
- if (staggerEase) values = values.map(val => staggerEase(val / maxValue) * maxValue);
9996
- if (reversed) values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
9997
- if (fromRandom) values = shuffle(values);
10216
+ maxValue = values[0];
10217
+ for (let k = 1; k < total; k++) if (values[k] > maxValue) maxValue = values[k];
10218
+ if (staggerEase || reversed) {
10219
+ for (let k = 0; k < total; k++) {
10220
+ let v = values[k];
10221
+ if (staggerEase) v = staggerEase(v / maxValue) * maxValue;
10222
+ if (reversed) v = axis ? -v : abs(maxValue - v);
10223
+ values[k] = v;
10224
+ }
10225
+ }
10226
+ if (hasJitter) {
10227
+ jitterSamples = new Array(total);
10228
+ for (let k = 0; k < total; k++) jitterSamples[k] = rng(-1, 1, 4);
10229
+ }
10230
+ if (fromRandom) values = shuffle(values, rng);
9998
10231
  }
9999
10232
  const spacing = isRange ? (val2 - val1) / maxValue : val1;
10000
10233
  if (isUnd(cachedOffset)) {
@@ -10002,6 +10235,11 @@ const stagger = (val, params = {}) => {
10002
10235
  }
10003
10236
  /** @type {String|Number} */
10004
10237
  let output = cachedOffset + ((spacing * round$1(values[staggerIndex], 2)) || 0);
10238
+ if (hasJitter) {
10239
+ const progress = maxValue ? values[staggerIndex] / maxValue : 0;
10240
+ const mag = jitterStart + (jitterEnd - jitterStart) * progress;
10241
+ output = /** @type {Number} */(output) + jitterSamples[staggerIndex] * mag;
10242
+ }
10005
10243
  if (params.modifier) output = params.modifier(/** @type {Number} */(output));
10006
10244
  if (unitMatch) output = `${output}${unitMatch[2]}`;
10007
10245
  return output;
@@ -10371,6 +10609,7 @@ const filterLineElements = ($el, lineIndex, bin) => {
10371
10609
  */
10372
10610
  const generateTemplate = (type, params = {}) => {
10373
10611
  let template = ``;
10612
+ if (!params) params = {};
10374
10613
  const classString = isStr(params.class) ? ` class="${params.class}"` : '';
10375
10614
  const cloneType = setValue(params.clone, false);
10376
10615
  const wrapType = setValue(params.wrap, false);
@@ -10752,6 +10991,16 @@ const split = (target, parameters) => {
10752
10991
 
10753
10992
 
10754
10993
 
10994
+ /**
10995
+ * @typedef {Object} ScrambleTextTween
10996
+ * @property {Number} from
10997
+ * @property {Number} to
10998
+ * @property {Number} duration
10999
+ * @property {Number} delay
11000
+ * @property {String} ease
11001
+ * @property {(v: Number) => String} modifier
11002
+ */
11003
+
10755
11004
  /**
10756
11005
  * '-' 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-')
10757
11006
  * @param {String} str
@@ -10789,7 +11038,7 @@ const originalTexts = new WeakMap();
10789
11038
  * progressively revealing the original text.
10790
11039
  *
10791
11040
  * @param {ScrambleTextParams} [params]
10792
- * @return {FunctionValue}
11041
+ * @return {FunctionValue<ScrambleTextTween>}
10793
11042
  */
10794
11043
  const scrambleText = (params = {}) => {
10795
11044
  if (!params) params = {};