animejs 4.3.5 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +9 -12
  2. package/dist/bundles/anime.esm.js +1048 -421
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +1053 -421
  5. package/dist/bundles/anime.umd.min.js +2 -2
  6. package/dist/modules/animatable/animatable.cjs +1 -1
  7. package/dist/modules/animatable/animatable.js +2 -2
  8. package/dist/modules/animatable/index.cjs +1 -1
  9. package/dist/modules/animatable/index.js +1 -1
  10. package/dist/modules/animation/additive.cjs +1 -1
  11. package/dist/modules/animation/additive.js +1 -1
  12. package/dist/modules/animation/animation.cjs +38 -16
  13. package/dist/modules/animation/animation.d.ts +2 -2
  14. package/dist/modules/animation/animation.js +42 -20
  15. package/dist/modules/animation/composition.cjs +1 -1
  16. package/dist/modules/animation/composition.js +3 -3
  17. package/dist/modules/animation/index.cjs +1 -1
  18. package/dist/modules/animation/index.js +1 -1
  19. package/dist/modules/core/clock.cjs +1 -1
  20. package/dist/modules/core/clock.js +1 -1
  21. package/dist/modules/core/colors.cjs +1 -1
  22. package/dist/modules/core/colors.js +1 -1
  23. package/dist/modules/core/consts.cjs +3 -9
  24. package/dist/modules/core/consts.d.ts +1 -5
  25. package/dist/modules/core/consts.js +4 -8
  26. package/dist/modules/core/globals.cjs +16 -5
  27. package/dist/modules/core/globals.d.ts +22 -1
  28. package/dist/modules/core/globals.js +18 -6
  29. package/dist/modules/core/helpers.cjs +7 -10
  30. package/dist/modules/core/helpers.js +8 -11
  31. package/dist/modules/core/render.cjs +66 -63
  32. package/dist/modules/core/render.js +68 -65
  33. package/dist/modules/core/styles.cjs +53 -32
  34. package/dist/modules/core/styles.d.ts +1 -0
  35. package/dist/modules/core/styles.js +55 -35
  36. package/dist/modules/core/targets.cjs +1 -1
  37. package/dist/modules/core/targets.js +1 -1
  38. package/dist/modules/core/transforms.cjs +129 -13
  39. package/dist/modules/core/transforms.d.ts +1 -0
  40. package/dist/modules/core/transforms.js +130 -15
  41. package/dist/modules/core/units.cjs +1 -1
  42. package/dist/modules/core/units.js +1 -1
  43. package/dist/modules/core/values.cjs +68 -8
  44. package/dist/modules/core/values.d.ts +5 -2
  45. package/dist/modules/core/values.js +69 -11
  46. package/dist/modules/draggable/draggable.cjs +1 -1
  47. package/dist/modules/draggable/draggable.js +1 -1
  48. package/dist/modules/draggable/index.cjs +1 -1
  49. package/dist/modules/draggable/index.js +1 -1
  50. package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
  51. package/dist/modules/easings/cubic-bezier/index.js +1 -1
  52. package/dist/modules/easings/eases/index.cjs +1 -1
  53. package/dist/modules/easings/eases/index.js +1 -1
  54. package/dist/modules/easings/eases/parser.cjs +1 -1
  55. package/dist/modules/easings/eases/parser.js +1 -1
  56. package/dist/modules/easings/index.cjs +1 -1
  57. package/dist/modules/easings/index.js +1 -1
  58. package/dist/modules/easings/irregular/index.cjs +1 -1
  59. package/dist/modules/easings/irregular/index.js +1 -1
  60. package/dist/modules/easings/linear/index.cjs +1 -1
  61. package/dist/modules/easings/linear/index.js +1 -1
  62. package/dist/modules/easings/none.cjs +1 -1
  63. package/dist/modules/easings/none.js +1 -1
  64. package/dist/modules/easings/spring/index.cjs +1 -1
  65. package/dist/modules/easings/spring/index.js +1 -1
  66. package/dist/modules/easings/steps/index.cjs +1 -1
  67. package/dist/modules/easings/steps/index.js +1 -1
  68. package/dist/modules/engine/engine.cjs +1 -1
  69. package/dist/modules/engine/engine.js +1 -1
  70. package/dist/modules/engine/index.cjs +1 -1
  71. package/dist/modules/engine/index.js +1 -1
  72. package/dist/modules/events/index.cjs +1 -1
  73. package/dist/modules/events/index.js +1 -1
  74. package/dist/modules/events/scroll.cjs +1 -1
  75. package/dist/modules/events/scroll.js +1 -1
  76. package/dist/modules/index.cjs +9 -1
  77. package/dist/modules/index.d.ts +1 -0
  78. package/dist/modules/index.js +4 -1
  79. package/dist/modules/layout/index.cjs +1 -1
  80. package/dist/modules/layout/index.js +1 -1
  81. package/dist/modules/layout/layout.cjs +47 -24
  82. package/dist/modules/layout/layout.d.ts +4 -3
  83. package/dist/modules/layout/layout.js +48 -25
  84. package/dist/modules/scope/index.cjs +1 -1
  85. package/dist/modules/scope/index.js +1 -1
  86. package/dist/modules/scope/scope.cjs +1 -1
  87. package/dist/modules/scope/scope.js +1 -1
  88. package/dist/modules/svg/drawable.cjs +1 -1
  89. package/dist/modules/svg/drawable.js +1 -1
  90. package/dist/modules/svg/helpers.cjs +1 -1
  91. package/dist/modules/svg/helpers.js +1 -1
  92. package/dist/modules/svg/index.cjs +1 -1
  93. package/dist/modules/svg/index.js +1 -1
  94. package/dist/modules/svg/morphto.cjs +3 -6
  95. package/dist/modules/svg/morphto.js +3 -6
  96. package/dist/modules/svg/motionpath.cjs +1 -1
  97. package/dist/modules/svg/motionpath.js +1 -1
  98. package/dist/modules/text/index.cjs +3 -1
  99. package/dist/modules/text/index.d.ts +1 -0
  100. package/dist/modules/text/index.js +2 -1
  101. package/dist/modules/text/scramble.cjs +272 -0
  102. package/dist/modules/text/scramble.d.ts +3 -0
  103. package/dist/modules/text/scramble.js +270 -0
  104. package/dist/modules/text/split.cjs +5 -5
  105. package/dist/modules/text/split.d.ts +5 -5
  106. package/dist/modules/text/split.js +5 -5
  107. package/dist/modules/timeline/index.cjs +1 -1
  108. package/dist/modules/timeline/index.js +1 -1
  109. package/dist/modules/timeline/position.cjs +1 -1
  110. package/dist/modules/timeline/position.js +1 -1
  111. package/dist/modules/timeline/timeline.cjs +36 -18
  112. package/dist/modules/timeline/timeline.d.ts +6 -5
  113. package/dist/modules/timeline/timeline.js +37 -19
  114. package/dist/modules/timer/index.cjs +1 -1
  115. package/dist/modules/timer/index.js +1 -1
  116. package/dist/modules/timer/timer.cjs +8 -12
  117. package/dist/modules/timer/timer.d.ts +2 -0
  118. package/dist/modules/timer/timer.js +9 -13
  119. package/dist/modules/types/index.d.ts +76 -8
  120. package/dist/modules/utils/chainable.cjs +8 -5
  121. package/dist/modules/utils/chainable.js +8 -5
  122. package/dist/modules/utils/index.cjs +5 -1
  123. package/dist/modules/utils/index.d.ts +1 -0
  124. package/dist/modules/utils/index.js +2 -1
  125. package/dist/modules/utils/number.cjs +1 -1
  126. package/dist/modules/utils/number.js +1 -1
  127. package/dist/modules/utils/random.cjs +1 -1
  128. package/dist/modules/utils/random.js +1 -1
  129. package/dist/modules/utils/stagger.cjs +117 -20
  130. package/dist/modules/utils/stagger.js +118 -21
  131. package/dist/modules/utils/target.cjs +1 -1
  132. package/dist/modules/utils/target.js +1 -1
  133. package/dist/modules/utils/time.cjs +5 -3
  134. package/dist/modules/utils/time.d.ts +1 -1
  135. package/dist/modules/utils/time.js +5 -3
  136. package/dist/modules/waapi/composition.cjs +1 -1
  137. package/dist/modules/waapi/composition.js +1 -1
  138. package/dist/modules/waapi/index.cjs +1 -1
  139. package/dist/modules/waapi/index.js +1 -1
  140. package/dist/modules/waapi/waapi.cjs +19 -20
  141. package/dist/modules/waapi/waapi.js +20 -21
  142. package/package.json +2 -1
@@ -8,6 +8,17 @@
8
8
  * Scope,
9
9
  * } from '../scope/index.js'
10
10
  */
11
+ /**
12
+ * @typedef {Object} EditorGlobals
13
+ * @property {boolean} showPanel
14
+ * @property {boolean} synced
15
+ * @property {Function} addAnimation
16
+ * @property {Function} addTimeline
17
+ * @property {Function} addTimelineChild
18
+ * @property {Function} resolveStagger
19
+ * @property {Object|null} _head
20
+ * @property {Object|null} _tail
21
+ */
11
22
  /** @type {DefaultsParams} */
12
23
  export const defaults: DefaultsParams;
13
24
  export namespace scope {
@@ -19,12 +30,22 @@ export namespace globals {
19
30
  export let precision: number;
20
31
  export let timeScale: number;
21
32
  export let tickThreshold: number;
33
+ export let editor: EditorGlobals | null;
22
34
  }
23
- export const devTools: any;
24
35
  export namespace globalVersions {
25
36
  let version: string;
26
37
  let engine: any;
27
38
  }
39
+ export type EditorGlobals = {
40
+ showPanel: boolean;
41
+ synced: boolean;
42
+ addAnimation: Function;
43
+ addTimeline: Function;
44
+ addTimelineChild: Function;
45
+ resolveStagger: Function;
46
+ _head: any | null;
47
+ _tail: any | null;
48
+ };
28
49
  import type { DefaultsParams } from '../types/index.js';
29
50
  import type { Scope } from '../scope/index.js';
30
51
  import { doc } from './consts.js';
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
7
7
 
8
- import { isBrowser, win, noop, maxFps, K, compositionTypes, doc } from './consts.js';
8
+ import { isBrowser, win, noop, compositionTypes, K, maxFps, doc } from './consts.js';
9
9
 
10
10
  /**
11
11
  * @import {
@@ -18,6 +18,18 @@ import { isBrowser, win, noop, maxFps, K, compositionTypes, doc } from './consts
18
18
  * } from '../scope/index.js'
19
19
  */
20
20
 
21
+ /**
22
+ * @typedef {Object} EditorGlobals
23
+ * @property {boolean} showPanel
24
+ * @property {boolean} synced
25
+ * @property {Function} addAnimation
26
+ * @property {Function} addTimeline
27
+ * @property {Function} addTimelineChild
28
+ * @property {Function} resolveStagger
29
+ * @property {Object|null} _head
30
+ * @property {Object|null} _tail
31
+ */
32
+
21
33
  /** @type {DefaultsParams} */
22
34
  const defaults = {
23
35
  id: null,
@@ -61,15 +73,15 @@ const globals = {
61
73
  timeScale: 1,
62
74
  /** @type {Number} */
63
75
  tickThreshold: 200,
76
+ /** @type {EditorGlobals|null} */
77
+ editor: null,
64
78
  };
65
79
 
66
- const devTools = isBrowser && win.AnimeJSDevTools;
67
-
68
- const globalVersions = { version: '4.3.5', engine: null };
80
+ const globalVersions = { version: '4.4.0', engine: null };
69
81
 
70
82
  if (isBrowser) {
71
83
  if (!win.AnimeJS) win.AnimeJS = [];
72
84
  win.AnimeJS.push(globalVersions);
73
85
  }
74
86
 
75
- export { defaults, devTools, globalVersions, globals, scope };
87
+ export { defaults, globalVersions, globals, scope };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -124,8 +124,6 @@ const _round = Math.round;
124
124
  */
125
125
  const clamp = (v, min, max) => v < min ? min : v > max ? max : v;
126
126
 
127
- const powCache = {};
128
-
129
127
  /**
130
128
  * Rounds a number to specified decimal places
131
129
  *
@@ -133,13 +131,12 @@ const powCache = {};
133
131
  * @param {Number} decimalLength - Number of decimal places
134
132
  * @return {Number}
135
133
  */
136
- const round = (v, decimalLength) => {
137
- if (decimalLength < 0) return v;
138
- if (!decimalLength) return _round(v);
139
- let p = powCache[decimalLength];
140
- if (!p) p = powCache[decimalLength] = 10 ** decimalLength;
141
- return _round(v * p) / p;
142
- };
134
+ const round = (v, decimalLength) => {
135
+ if (decimalLength < 0) return v;
136
+ if (!decimalLength) return _round(v);
137
+ const p = 10 ** decimalLength;
138
+ return _round(v * p) / p;
139
+ };
143
140
 
144
141
  /**
145
142
  * Snaps a value to nearest increment or array value
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
7
7
 
8
- import { isBrowser, maxValue, minValue, hexTestRgx, lowerCaseRgx, validRgbHslRgx } from './consts.js';
8
+ import { isBrowser, maxValue, minValue, lowerCaseRgx, hexTestRgx, validRgbHslRgx } from './consts.js';
9
9
  import { globals } from './globals.js';
10
10
 
11
11
  /**
@@ -122,8 +122,6 @@ const _round = Math.round;
122
122
  */
123
123
  const clamp = (v, min, max) => v < min ? min : v > max ? max : v;
124
124
 
125
- const powCache = {};
126
-
127
125
  /**
128
126
  * Rounds a number to specified decimal places
129
127
  *
@@ -131,13 +129,12 @@ const powCache = {};
131
129
  * @param {Number} decimalLength - Number of decimal places
132
130
  * @return {Number}
133
131
  */
134
- const round = (v, decimalLength) => {
135
- if (decimalLength < 0) return v;
136
- if (!decimalLength) return _round(v);
137
- let p = powCache[decimalLength];
138
- if (!p) p = powCache[decimalLength] = 10 ** decimalLength;
139
- return _round(v * p) / p;
140
- };
132
+ const round = (v, decimalLength) => {
133
+ if (decimalLength < 0) return v;
134
+ if (!decimalLength) return _round(v);
135
+ const p = 10 ** decimalLength;
136
+ return _round(v * p) / p;
137
+ };
141
138
 
142
139
  /**
143
140
  * Snaps a value to nearest increment or array value
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -10,9 +10,11 @@
10
10
  var globals = require('./globals.cjs');
11
11
  var consts = require('./consts.cjs');
12
12
  var helpers = require('./helpers.cjs');
13
+ var transforms = require('./transforms.cjs');
14
+ var values = require('./values.cjs');
13
15
 
14
16
  /**
15
- * @import {
17
+ * @import {
16
18
  * Tickable,
17
19
  * Renderable,
18
20
  * CallbackArgument,
@@ -55,7 +57,6 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
55
57
  const _hasChildren = tickable._hasChildren;
56
58
  const tickableDelay = tickable._delay;
57
59
  const tickablePrevAbsoluteTime = tickable._currentTime; // TODO: rename ._currentTime to ._absoluteCurrentTime
58
-
59
60
  const tickableEndTime = tickableDelay + iterationDuration;
60
61
  const tickableAbsoluteTime = time - tickableDelay;
61
62
  const tickablePrevTime = helpers.clamp(tickablePrevAbsoluteTime, -tickableDelay, duration);
@@ -187,30 +188,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
187
188
  number = /** @type {Number} */(tweenModifier(helpers.round(helpers.lerp(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
188
189
  value = `${number}${tween._unit}`;
189
190
  } else if (tweenValueType === consts.valueTypes.COLOR) {
190
- const fn = tween._fromNumbers;
191
- const tn = tween._toNumbers;
192
- const r = helpers.round(helpers.clamp(/** @type {Number} */(tweenModifier(helpers.lerp(fn[0], tn[0], tweenProgress))), 0, 255), 0);
193
- const g = helpers.round(helpers.clamp(/** @type {Number} */(tweenModifier(helpers.lerp(fn[1], tn[1], tweenProgress))), 0, 255), 0);
194
- const b = helpers.round(helpers.clamp(/** @type {Number} */(tweenModifier(helpers.lerp(fn[2], tn[2], tweenProgress))), 0, 255), 0);
195
- const a = helpers.clamp(/** @type {Number} */(tweenModifier(helpers.round(helpers.lerp(fn[3], tn[3], tweenProgress), tweenPrecision))), 0, 1);
196
- value = `rgba(${r},${g},${b},${a})`;
197
- if (tweenHasComposition) {
198
- const ns = tween._numbers;
199
- ns[0] = r;
200
- ns[1] = g;
201
- ns[2] = b;
202
- ns[3] = a;
203
- }
191
+ value = values.composeColorValue(tween, tweenProgress, tweenPrecision);
204
192
  } else if (tweenValueType === consts.valueTypes.COMPLEX) {
205
- value = tween._strings[0];
206
- for (let j = 0, l = tween._toNumbers.length; j < l; j++) {
207
- const n = /** @type {Number} */(tweenModifier(helpers.round(helpers.lerp(tween._fromNumbers[j], tween._toNumbers[j], tweenProgress), tweenPrecision)));
208
- const s = tween._strings[j + 1];
209
- value += `${s ? n + s : n}`;
210
- if (tweenHasComposition) {
211
- tween._numbers[j] = n;
212
- }
213
- }
193
+ value = values.composeComplexValue(tween, tweenProgress, tweenPrecision);
214
194
  }
215
195
 
216
196
  // For additive tweens and Animatables
@@ -253,14 +233,8 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
253
233
 
254
234
  }
255
235
 
256
- // NOTE: Possible improvement: Use translate(x,y) / translate3d(x,y,z) syntax
257
- // to reduce memory usage on string composition
258
236
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
259
- let str = consts.emptyString;
260
- for (let key in tweenTargetTransformsProperties) {
261
- str += `${consts.transformsFragmentStrings[key]}${tweenTargetTransformsProperties[key]}) `;
262
- }
263
- tweenStyle.transform = str;
237
+ tweenStyle.transform = transforms.buildTransformString(tweenTargetTransformsProperties);
264
238
  tweenTransformsNeedUpdate = 0;
265
239
  }
266
240
 
@@ -317,6 +291,50 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
317
291
  return hasRendered;
318
292
  };
319
293
 
294
+ // Shared context for extracted forEachChildren callbacks in tick()
295
+ // Avoids closure allocation every frame
296
+
297
+ let renderCtxChildrenTime = 0;
298
+ let renderCtxTlFps = 0;
299
+ let renderCtxTickTime = 0;
300
+ let renderCtxTickMode = 0;
301
+ let renderCtxMuteCallbacks = 0;
302
+ let renderCtxInternalRender = 0;
303
+ let renderCtxChildrenHasRendered = 0;
304
+ let renderCtxChildrenHaveCompleted = true;
305
+ let loopCtxIsRunningBackwards = false;
306
+ let loopCtxIterationDuration = 0;
307
+ let loopCtxMuteCallbacks = 0;
308
+
309
+ /** @param {JSAnimation} child */
310
+ const tickLoopChild = (child) => {
311
+ if (!loopCtxIsRunningBackwards) {
312
+ // Force an internal render to trigger the callbacks if the child has not completed on loop
313
+ if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
314
+ render(child, loopCtxIterationDuration, loopCtxMuteCallbacks, 1, consts.tickModes.FORCE);
315
+ }
316
+ // Reset their began and completed flags to allow retrigering callbacks on the next iteration
317
+ child.began = false;
318
+ child.completed = false;
319
+ } else {
320
+ const childDuration = child.duration;
321
+ const childStartTime = child._offset + child._delay;
322
+ const childEndTime = childStartTime + childDuration;
323
+ // Triggers the onComplete callback on reverse for children on the edges of the timeline
324
+ if (!loopCtxMuteCallbacks && childDuration <= consts.minValue && (!childStartTime || childEndTime === loopCtxIterationDuration)) {
325
+ child.onComplete(child);
326
+ }
327
+ }
328
+ };
329
+
330
+ /** @param {JSAnimation} child */
331
+ const tickRenderChild = (child) => {
332
+ const childTime = helpers.round((renderCtxChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
333
+ const childTickMode = child._fps < renderCtxTlFps ? child.requestTick(renderCtxTickTime) : renderCtxTickMode;
334
+ renderCtxChildrenHasRendered += render(child, childTime, renderCtxMuteCallbacks, renderCtxInternalRender, childTickMode);
335
+ if (!child.completed && renderCtxChildrenHaveCompleted) renderCtxChildrenHaveCompleted = false;
336
+ };
337
+
320
338
  /**
321
339
  * @param {Tickable} tickable
322
340
  * @param {Number} time
@@ -333,45 +351,30 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
333
351
  const tlIsRunningBackwards = tl.backwards;
334
352
  const tlChildrenTime = internalRender ? time : tl._iterationTime;
335
353
  const tlCildrenTickTime = helpers.now();
336
-
337
354
  let tlChildrenHasRendered = 0;
338
355
  let tlChildrenHaveCompleted = true;
339
-
340
356
  // If the timeline has looped forward, we need to manually triggers children skipped callbacks
341
357
  if (!internalRender && tl._currentIteration !== _currentIteration) {
342
358
  const tlIterationDuration = tl.iterationDuration;
343
- helpers.forEachChildren(tl, (/** @type {JSAnimation} */child) => {
344
- if (!tlIsRunningBackwards) {
345
- // Force an internal render to trigger the callbacks if the child has not completed on loop
346
- if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
347
- render(child, tlIterationDuration, muteCallbacks, 1, consts.tickModes.FORCE);
348
- }
349
- // Reset their began and completed flags to allow retrigering callbacks on the next iteration
350
- child.began = false;
351
- child.completed = false;
352
- } else {
353
- const childDuration = child.duration;
354
- const childStartTime = child._offset + child._delay;
355
- const childEndTime = childStartTime + childDuration;
356
- // Triggers the onComplete callback on reverse for children on the edges of the timeline
357
- if (!muteCallbacks && childDuration <= consts.minValue && (!childStartTime || childEndTime === tlIterationDuration)) {
358
- child.onComplete(child);
359
- }
360
- }
361
- });
359
+ loopCtxIsRunningBackwards = tlIsRunningBackwards;
360
+ loopCtxIterationDuration = tlIterationDuration;
361
+ loopCtxMuteCallbacks = muteCallbacks;
362
+ helpers.forEachChildren(tl, tickLoopChild);
362
363
  if (!muteCallbacks) tl.onLoop(/** @type {CallbackArgument} */(tl));
363
364
  }
364
-
365
- helpers.forEachChildren(tl, (/** @type {JSAnimation} */child) => {
366
- const childTime = helpers.round((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
367
- const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
368
- tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
369
- if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
370
- }, tlIsRunningBackwards);
371
-
365
+ renderCtxChildrenTime = tlChildrenTime;
366
+ renderCtxTlFps = tl._fps;
367
+ renderCtxTickTime = tlCildrenTickTime;
368
+ renderCtxTickMode = tickMode;
369
+ renderCtxMuteCallbacks = muteCallbacks;
370
+ renderCtxInternalRender = internalRender;
371
+ renderCtxChildrenHasRendered = 0;
372
+ renderCtxChildrenHaveCompleted = true;
373
+ helpers.forEachChildren(tl, tickRenderChild, tlIsRunningBackwards);
374
+ tlChildrenHasRendered = renderCtxChildrenHasRendered;
375
+ tlChildrenHaveCompleted = renderCtxChildrenHaveCompleted;
372
376
  // Renders on timeline are triggered by its children so it needs to be set after rendering the children
373
377
  if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
374
-
375
378
  // Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
376
379
  if ((tlChildrenHaveCompleted || tlIsRunningBackwards) && tl._currentTime >= tl.duration) {
377
380
  // Make sure the paused flag is false in case it has been skipped in the render function
@@ -1,16 +1,18 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
7
7
 
8
8
  import { globals } from './globals.js';
9
- import { minValue, tickModes, valueTypes, compositionTypes, tweenTypes, transformsSymbol, transformsFragmentStrings, emptyString } from './consts.js';
10
- import { forEachChildren, round, now, clamp, lerp } from './helpers.js';
9
+ import { tickModes, valueTypes, compositionTypes, tweenTypes, transformsSymbol, minValue } from './consts.js';
10
+ import { forEachChildren, clamp, round, lerp, now } from './helpers.js';
11
+ import { buildTransformString } from './transforms.js';
12
+ import { composeColorValue, composeComplexValue } from './values.js';
11
13
 
12
14
  /**
13
- * @import {
15
+ * @import {
14
16
  * Tickable,
15
17
  * Renderable,
16
18
  * CallbackArgument,
@@ -53,7 +55,6 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
53
55
  const _hasChildren = tickable._hasChildren;
54
56
  const tickableDelay = tickable._delay;
55
57
  const tickablePrevAbsoluteTime = tickable._currentTime; // TODO: rename ._currentTime to ._absoluteCurrentTime
56
-
57
58
  const tickableEndTime = tickableDelay + iterationDuration;
58
59
  const tickableAbsoluteTime = time - tickableDelay;
59
60
  const tickablePrevTime = clamp(tickablePrevAbsoluteTime, -tickableDelay, duration);
@@ -185,30 +186,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
185
186
  number = /** @type {Number} */(tweenModifier(round(lerp(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
186
187
  value = `${number}${tween._unit}`;
187
188
  } else if (tweenValueType === valueTypes.COLOR) {
188
- const fn = tween._fromNumbers;
189
- const tn = tween._toNumbers;
190
- const r = round(clamp(/** @type {Number} */(tweenModifier(lerp(fn[0], tn[0], tweenProgress))), 0, 255), 0);
191
- const g = round(clamp(/** @type {Number} */(tweenModifier(lerp(fn[1], tn[1], tweenProgress))), 0, 255), 0);
192
- const b = round(clamp(/** @type {Number} */(tweenModifier(lerp(fn[2], tn[2], tweenProgress))), 0, 255), 0);
193
- const a = clamp(/** @type {Number} */(tweenModifier(round(lerp(fn[3], tn[3], tweenProgress), tweenPrecision))), 0, 1);
194
- value = `rgba(${r},${g},${b},${a})`;
195
- if (tweenHasComposition) {
196
- const ns = tween._numbers;
197
- ns[0] = r;
198
- ns[1] = g;
199
- ns[2] = b;
200
- ns[3] = a;
201
- }
189
+ value = composeColorValue(tween, tweenProgress, tweenPrecision);
202
190
  } else if (tweenValueType === valueTypes.COMPLEX) {
203
- value = tween._strings[0];
204
- for (let j = 0, l = tween._toNumbers.length; j < l; j++) {
205
- const n = /** @type {Number} */(tweenModifier(round(lerp(tween._fromNumbers[j], tween._toNumbers[j], tweenProgress), tweenPrecision)));
206
- const s = tween._strings[j + 1];
207
- value += `${s ? n + s : n}`;
208
- if (tweenHasComposition) {
209
- tween._numbers[j] = n;
210
- }
211
- }
191
+ value = composeComplexValue(tween, tweenProgress, tweenPrecision);
212
192
  }
213
193
 
214
194
  // For additive tweens and Animatables
@@ -251,14 +231,8 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
251
231
 
252
232
  }
253
233
 
254
- // NOTE: Possible improvement: Use translate(x,y) / translate3d(x,y,z) syntax
255
- // to reduce memory usage on string composition
256
234
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
257
- let str = emptyString;
258
- for (let key in tweenTargetTransformsProperties) {
259
- str += `${transformsFragmentStrings[key]}${tweenTargetTransformsProperties[key]}) `;
260
- }
261
- tweenStyle.transform = str;
235
+ tweenStyle.transform = buildTransformString(tweenTargetTransformsProperties);
262
236
  tweenTransformsNeedUpdate = 0;
263
237
  }
264
238
 
@@ -315,6 +289,50 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
315
289
  return hasRendered;
316
290
  };
317
291
 
292
+ // Shared context for extracted forEachChildren callbacks in tick()
293
+ // Avoids closure allocation every frame
294
+
295
+ let renderCtxChildrenTime = 0;
296
+ let renderCtxTlFps = 0;
297
+ let renderCtxTickTime = 0;
298
+ let renderCtxTickMode = 0;
299
+ let renderCtxMuteCallbacks = 0;
300
+ let renderCtxInternalRender = 0;
301
+ let renderCtxChildrenHasRendered = 0;
302
+ let renderCtxChildrenHaveCompleted = true;
303
+ let loopCtxIsRunningBackwards = false;
304
+ let loopCtxIterationDuration = 0;
305
+ let loopCtxMuteCallbacks = 0;
306
+
307
+ /** @param {JSAnimation} child */
308
+ const tickLoopChild = (child) => {
309
+ if (!loopCtxIsRunningBackwards) {
310
+ // Force an internal render to trigger the callbacks if the child has not completed on loop
311
+ if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
312
+ render(child, loopCtxIterationDuration, loopCtxMuteCallbacks, 1, tickModes.FORCE);
313
+ }
314
+ // Reset their began and completed flags to allow retrigering callbacks on the next iteration
315
+ child.began = false;
316
+ child.completed = false;
317
+ } else {
318
+ const childDuration = child.duration;
319
+ const childStartTime = child._offset + child._delay;
320
+ const childEndTime = childStartTime + childDuration;
321
+ // Triggers the onComplete callback on reverse for children on the edges of the timeline
322
+ if (!loopCtxMuteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === loopCtxIterationDuration)) {
323
+ child.onComplete(child);
324
+ }
325
+ }
326
+ };
327
+
328
+ /** @param {JSAnimation} child */
329
+ const tickRenderChild = (child) => {
330
+ const childTime = round((renderCtxChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
331
+ const childTickMode = child._fps < renderCtxTlFps ? child.requestTick(renderCtxTickTime) : renderCtxTickMode;
332
+ renderCtxChildrenHasRendered += render(child, childTime, renderCtxMuteCallbacks, renderCtxInternalRender, childTickMode);
333
+ if (!child.completed && renderCtxChildrenHaveCompleted) renderCtxChildrenHaveCompleted = false;
334
+ };
335
+
318
336
  /**
319
337
  * @param {Tickable} tickable
320
338
  * @param {Number} time
@@ -331,45 +349,30 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
331
349
  const tlIsRunningBackwards = tl.backwards;
332
350
  const tlChildrenTime = internalRender ? time : tl._iterationTime;
333
351
  const tlCildrenTickTime = now();
334
-
335
352
  let tlChildrenHasRendered = 0;
336
353
  let tlChildrenHaveCompleted = true;
337
-
338
354
  // If the timeline has looped forward, we need to manually triggers children skipped callbacks
339
355
  if (!internalRender && tl._currentIteration !== _currentIteration) {
340
356
  const tlIterationDuration = tl.iterationDuration;
341
- forEachChildren(tl, (/** @type {JSAnimation} */child) => {
342
- if (!tlIsRunningBackwards) {
343
- // Force an internal render to trigger the callbacks if the child has not completed on loop
344
- if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
345
- render(child, tlIterationDuration, muteCallbacks, 1, tickModes.FORCE);
346
- }
347
- // Reset their began and completed flags to allow retrigering callbacks on the next iteration
348
- child.began = false;
349
- child.completed = false;
350
- } else {
351
- const childDuration = child.duration;
352
- const childStartTime = child._offset + child._delay;
353
- const childEndTime = childStartTime + childDuration;
354
- // Triggers the onComplete callback on reverse for children on the edges of the timeline
355
- if (!muteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === tlIterationDuration)) {
356
- child.onComplete(child);
357
- }
358
- }
359
- });
357
+ loopCtxIsRunningBackwards = tlIsRunningBackwards;
358
+ loopCtxIterationDuration = tlIterationDuration;
359
+ loopCtxMuteCallbacks = muteCallbacks;
360
+ forEachChildren(tl, tickLoopChild);
360
361
  if (!muteCallbacks) tl.onLoop(/** @type {CallbackArgument} */(tl));
361
362
  }
362
-
363
- forEachChildren(tl, (/** @type {JSAnimation} */child) => {
364
- const childTime = round((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
365
- const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
366
- tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
367
- if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
368
- }, tlIsRunningBackwards);
369
-
363
+ renderCtxChildrenTime = tlChildrenTime;
364
+ renderCtxTlFps = tl._fps;
365
+ renderCtxTickTime = tlCildrenTickTime;
366
+ renderCtxTickMode = tickMode;
367
+ renderCtxMuteCallbacks = muteCallbacks;
368
+ renderCtxInternalRender = internalRender;
369
+ renderCtxChildrenHasRendered = 0;
370
+ renderCtxChildrenHaveCompleted = true;
371
+ forEachChildren(tl, tickRenderChild, tlIsRunningBackwards);
372
+ tlChildrenHasRendered = renderCtxChildrenHasRendered;
373
+ tlChildrenHaveCompleted = renderCtxChildrenHaveCompleted;
370
374
  // Renders on timeline are triggered by its children so it needs to be set after rendering the children
371
375
  if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
372
-
373
376
  // Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
374
377
  if ((tlChildrenHaveCompleted || tlIsRunningBackwards) && tl._currentTime >= tl.duration) {
375
378
  // Make sure the paused flag is false in case it has been skipped in the render function