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 - core - ESM
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -9,7 +9,7 @@ import { globals } from './globals.js';
9
9
  import { minValue, tickModes, valueTypes, compositionTypes, tweenTypes, transformsSymbol } from './consts.js';
10
10
  import { forEachChildren, round, now, clamp, lerp } from './helpers.js';
11
11
  import { buildTransformString } from './transforms.js';
12
- import { composeColorValue, composeComplexValue } from './values.js';
12
+ import { composeComplexValue } from './values.js';
13
13
 
14
14
  /**
15
15
  * @import {
@@ -74,12 +74,14 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
74
74
  // Execute the "expensive" iterations calculations only when necessary
75
75
  if (iterationCount > 1) {
76
76
  // bitwise NOT operator seems to be generally faster than Math.floor() across browsers
77
- const currentIteration = ~~(tickableCurrentTime / (iterationDuration + (isCurrentTimeEqualOrAboveDuration ? 0 : _loopDelay)));
77
+ const period = iterationDuration + (isCurrentTimeEqualOrAboveDuration ? 0 : _loopDelay);
78
+ const currentIteration = ~~(tickableCurrentTime / period);
78
79
  tickable._currentIteration = clamp(currentIteration, 0, iterationCount);
79
80
  // Prevent the iteration count to go above the max iterations when reaching the end of the animation
80
81
  if (isCurrentTimeEqualOrAboveDuration) tickable._currentIteration--;
81
82
  isOdd = tickable._currentIteration % 2;
82
- iterationElapsedTime = tickableCurrentTime % (iterationDuration + _loopDelay) || 0;
83
+ // 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.
84
+ iterationElapsedTime = tickableCurrentTime - currentIteration * period || 0;
83
85
  }
84
86
 
85
87
  // Checks if exactly one of _reversed and (_alternate && isOdd) is true
@@ -111,12 +113,14 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
111
113
  if (
112
114
  forcedTick ||
113
115
  tickMode === tickModes.AUTO && (
114
- time >= tickableDelay && time <= tickableEndTime || // Normal render
116
+ // Timeline children render from their offset instead of their delay so the gap left by a truncated sibling is covered on seek.
117
+ time >= (parent && tickableDelay > 0 ? 0 : tickableDelay) && time <= tickableEndTime || // Normal render
115
118
  time <= tickableDelay && tickablePrevTime > tickableDelay || // Playhead is before the animation start time so make sure the animation is at its initial state
116
119
  time >= tickableEndTime && tickablePrevTime !== duration // Playhead is after the animation end time so make sure the animation is at its end state
117
120
  ) ||
118
121
  iterationTime >= tickableEndTime && tickablePrevTime !== duration ||
119
- iterationTime <= tickableDelay && tickablePrevTime > 0 ||
122
+ // 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.
123
+ iterationTime <= tickableDelay && tickablePrevTime > 0 && !isCurrentTimeEqualOrAboveDuration ||
120
124
  time <= tickablePrevTime && tickablePrevTime === duration && completed || // Force a render if a seek occurs on an completed animation
121
125
  isCurrentTimeEqualOrAboveDuration && !completed && isSetter // This prevents 0 duration tickables to be skipped
122
126
  ) {
@@ -132,7 +136,8 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
132
136
 
133
137
  // Time has jumped more than globals.tickThreshold so consider this tick manual
134
138
  const forcedRender = forcedTick || (isRunningBackwards ? deltaTime * -1 : deltaTime) >= globals.tickThreshold;
135
- const absoluteTime = tickable._offset + (parent ? parent._offset : 0) + tickableDelay + iterationTime;
139
+ // Round to match the precision of tween._absoluteStartTime so equal-time boundary checks compare cleanly without floating point drift from the unrounded _offset.
140
+ const absoluteTime = round(tickable._offset + (parent ? parent._offset : 0) + tickableDelay + iterationTime, 12);
136
141
 
137
142
  // Only Animation can have tweens, Timer returns undefined
138
143
  let tween = /** @type {Tween} */(/** @type {JSAnimation} */(tickable)._head);
@@ -151,15 +156,38 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
151
156
  const tweenNextRep = tween._nextRep;
152
157
  const tweenPrevRep = tween._prevRep;
153
158
  const tweenHasComposition = tweenComposition !== compositionTypes.none;
159
+ // The previous sibling stops writing at its truncated end, so this tween takes over the hold from that point.
160
+ const tweenPrevRepEndTime = tweenPrevRep ? tweenPrevRep._absoluteStartTime + tweenPrevRep._changeDuration : 0;
161
+ const tweenPrevRepIsCrossParent = tweenPrevRep && tweenPrevRep.parent !== tween.parent;
162
+ // Same parent keyframes take over at their own start, end plus delay equals the next start by construction.
163
+ // Cross parent siblings take over at their update start.
164
+ // Negative delay siblings take over at their own start instead.
165
+ const tweenNextRepTakeover = !tweenNextRep || tweenNextRep._isOverridden ? tweenAbsEndTime :
166
+ tweenNextRep.parent === tween.parent ? tweenAbsEndTime + tweenNextRep._delay :
167
+ tweenNextRep._absoluteStartTime < tweenNextRep._absoluteUpdateStartTime ? tweenNextRep._absoluteStartTime : tweenNextRep._absoluteUpdateStartTime;
154
168
 
155
169
  if ((forcedRender || (
156
- (tweenCurrentTime !== tweenChangeDuration || absoluteTime <= tweenAbsEndTime + (tweenNextRep ? tweenNextRep._delay : 0)) &&
157
- (tweenCurrentTime !== 0 || absoluteTime >= tween._absoluteStartTime)
158
- )) && (!tweenHasComposition || (
170
+ // 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.
171
+ (tweenCurrentTime !== tweenChangeDuration || absoluteTime <= tweenNextRepTakeover ||
172
+ (tweenPrevRep && !tweenPrevRepIsCrossParent && (!tweenNextRep || tweenNextRep.parent !== tween.parent))) &&
173
+ // A cross parent tween re-renders its from value from the previous sibling truncated end so the handoff gap holds.
174
+ // 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.
175
+ (tweenCurrentTime !== 0 || absoluteTime >= tween._absoluteStartTime ||
176
+ (tweenPrevRepIsCrossParent && !tween._hasFromValue && !tweenPrevRep._isOverridden && absoluteTime >= tweenPrevRepEndTime) ||
177
+ (tweenNextRep && !tweenNextRep._isOverridden && tweenNextRep.parent === tween.parent && tweenNextRep._currentTime !== 0 && iterationTime < tweenNextRep._startTime))
178
+ )) &&
179
+ // 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.
180
+ (!tweenPrevRep || tweenPrevRepIsCrossParent || iterationTime >= tween._startTime) &&
181
+ (!tweenHasComposition || (
159
182
  !tween._isOverridden &&
160
183
  (!tween._isOverlapped || absoluteTime <= tweenAbsEndTime) &&
161
- (!tweenNextRep || (tweenNextRep._isOverridden || absoluteTime <= tweenNextRep._absoluteStartTime)) &&
162
- (!tweenPrevRep || (tweenPrevRep._isOverridden || (absoluteTime >= (tweenPrevRep._absoluteStartTime + tweenPrevRep._changeDuration) + tween._delay)))
184
+ // The next sibling owns the value past its takeover point, so yielding there keeps writes single owner in both directions.
185
+ (!tweenNextRep || tweenNextRep._isOverridden || absoluteTime <= tweenNextRepTakeover) &&
186
+ // The previous sibling owns the value up to its truncated end.
187
+ // Cross parent tweens take over the hold from that point, explicit from values wait for their own start.
188
+ (!tweenPrevRep || (tweenPrevRep._isOverridden || (!tweenPrevRepIsCrossParent ?
189
+ absoluteTime >= tweenPrevRepEndTime + tween._delay :
190
+ absoluteTime >= tween._absoluteStartTime || (!tween._hasFromValue && absoluteTime >= tweenPrevRepEndTime))))
163
191
  ))
164
192
  ) {
165
193
 
@@ -170,7 +198,7 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
170
198
  const tweenType = tween._tweenType;
171
199
  const tweenIsObject = tweenType === tweenTypes.OBJECT;
172
200
  const tweenIsNumber = tweenValueType === valueTypes.NUMBER;
173
- // Only round the in-between frames values if the final value is a string
201
+ // 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.
174
202
  const tweenPrecision = (tweenIsNumber && tweenIsObject) || tweenProgress === 0 || tweenProgress === 1 ? -1 : globals.precision;
175
203
 
176
204
  // Recompose tween value
@@ -186,7 +214,22 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
186
214
  number = /** @type {Number} */(tweenModifier(round(lerp(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
187
215
  value = `${number}${tween._unit}`;
188
216
  } else if (tweenValueType === valueTypes.COLOR) {
189
- value = composeColorValue(tween, tweenProgress, tweenPrecision);
217
+ const ns = tween._numbers;
218
+ const fn = tween._fromNumbers;
219
+ const tn = tween._toNumbers;
220
+ const omt = 1 - tweenProgress;
221
+ const fr = fn[0], fg = fn[1], fb = fn[2];
222
+ const tr = tn[0], tg = tn[1], tb = tn[2];
223
+ // RGB channels lerp in pseudo-linear space (square inputs, sqrt result) to approximate gamma-correct blending.
224
+ // See https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear.
225
+ ns[0] = /** @type {Number} */(tweenModifier(Math.sqrt(fr * fr * omt + tr * tr * tweenProgress)));
226
+ ns[1] = /** @type {Number} */(tweenModifier(Math.sqrt(fg * fg * omt + tg * tg * tweenProgress)));
227
+ ns[2] = /** @type {Number} */(tweenModifier(Math.sqrt(fb * fb * omt + tb * tb * tweenProgress)));
228
+ ns[3] = /** @type {Number} */(tweenModifier(lerp(fn[3], tn[3], tweenProgress)));
229
+ // The rgba string is built only for the dispatch path or the internalRender composition tick (setters handles the color comp)
230
+ if (!tween._setter || internalRender) {
231
+ value = `rgba(${round(ns[0], 0)},${round(ns[1], 0)},${round(ns[2], 0)},${ns[3]})`;
232
+ }
190
233
  } else if (tweenValueType === valueTypes.COMPLEX) {
191
234
  value = composeComplexValue(tween, tweenProgress, tweenPrecision);
192
235
  }
@@ -201,7 +244,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
201
244
  const tweenProperty = tween.property;
202
245
  tweenTarget = tween.target;
203
246
 
204
- if (tweenIsObject) {
247
+ if (tween._setter) {
248
+ tween._setter(tweenTarget, number, tween);
249
+ } else if (tweenIsObject) {
205
250
  tweenTarget[tweenProperty] = value;
206
251
  } else if (tweenType === tweenTypes.ATTRIBUTE) {
207
252
  /** @type {DOMTarget} */(tweenTarget).setAttribute(tweenProperty, /** @type {String} */(value));
@@ -229,6 +274,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
229
274
  tween._value = value;
230
275
  }
231
276
 
277
+ } else if (tweenCurrentTime && tweenPrevRep && !tweenPrevRepIsCrossParent && iterationTime < tween._startTime) {
278
+ // Mark the keyframe as reverted when the playhead moves before its start, the previous keyframe owns the from revert and writes it once.
279
+ tween._currentTime = 0;
232
280
  }
233
281
 
234
282
  if (tweenTransformsNeedUpdate && tween._renderTransforms) {
@@ -336,6 +384,8 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
336
384
 
337
385
  forEachChildren(tl, (/** @type {JSAnimation} */child) => {
338
386
  const childTime = round((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
387
+ // 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.
388
+ if (tlIsRunningBackwards && childTime > child._delay + child.duration) return;
339
389
  const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
340
390
  tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
341
391
  if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -10,6 +10,7 @@
10
10
  var consts = require('./consts.cjs');
11
11
  var helpers = require('./helpers.cjs');
12
12
  var transforms = require('./transforms.cjs');
13
+ var values = require('./values.cjs');
13
14
 
14
15
  /**
15
16
  * @import {
@@ -76,7 +77,20 @@ const revertValues = (renderable, inlineStylesOnly = false) => {
76
77
  const tweenType = tween._tweenType;
77
78
  const originalInlinedValue = tween._inlineValue;
78
79
  const tweenHadNoInlineValue = helpers.isNil(originalInlinedValue) || originalInlinedValue === consts.emptyString;
79
- if (tweenType === consts.tweenTypes.OBJECT) {
80
+ if (tween._setter) {
81
+ if (!inlineStylesOnly && !tweenHadNoInlineValue) {
82
+ // 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.
83
+ values.decomposeRawValue(originalInlinedValue, values.decomposedOriginalValue);
84
+ if (values.decomposedOriginalValue.d) {
85
+ const src = values.decomposedOriginalValue.d;
86
+ const dst = tween._numbers;
87
+ for (let i = 0, l = src.length; i < l; i++) dst[i] = src[i];
88
+ } else {
89
+ tween._number = values.decomposedOriginalValue.n;
90
+ }
91
+ tween._setter(tween.target, tween._number, tween);
92
+ }
93
+ } else if (tweenType === consts.tweenTypes.OBJECT) {
80
94
  if (!inlineStylesOnly && !tweenHadNoInlineValue) {
81
95
  tweenTarget[tweenProperty] = originalInlinedValue;
82
96
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -8,6 +8,7 @@
8
8
  import { tweenTypes, isDomSymbol, transformsSymbol, emptyString, shortTransforms } from './consts.js';
9
9
  import { forEachChildren, toLowerCase, isNil, isSvg } from './helpers.js';
10
10
  import { buildTransformString } from './transforms.js';
11
+ import { decomposeRawValue, decomposedOriginalValue } from './values.js';
11
12
 
12
13
  /**
13
14
  * @import {
@@ -74,7 +75,20 @@ const revertValues = (renderable, inlineStylesOnly = false) => {
74
75
  const tweenType = tween._tweenType;
75
76
  const originalInlinedValue = tween._inlineValue;
76
77
  const tweenHadNoInlineValue = isNil(originalInlinedValue) || originalInlinedValue === emptyString;
77
- if (tweenType === tweenTypes.OBJECT) {
78
+ if (tween._setter) {
79
+ if (!inlineStylesOnly && !tweenHadNoInlineValue) {
80
+ // 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.
81
+ decomposeRawValue(originalInlinedValue, decomposedOriginalValue);
82
+ if (decomposedOriginalValue.d) {
83
+ const src = decomposedOriginalValue.d;
84
+ const dst = tween._numbers;
85
+ for (let i = 0, l = src.length; i < l; i++) dst[i] = src[i];
86
+ } else {
87
+ tween._number = decomposedOriginalValue.n;
88
+ }
89
+ tween._setter(tween.target, tween._number, tween);
90
+ }
91
+ } else if (tweenType === tweenTypes.OBJECT) {
78
92
  if (!inlineStylesOnly && !tweenHadNoInlineValue) {
79
93
  tweenTarget[tweenProperty] = originalInlinedValue;
80
94
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -113,18 +113,16 @@ function parseTargets(targets) {
113
113
  function registerTargets(targets) {
114
114
  const parsedTargetsArray = parseTargets(targets);
115
115
  const parsedTargetsLength = parsedTargetsArray.length;
116
- if (parsedTargetsLength) {
117
- for (let i = 0; i < parsedTargetsLength; i++) {
118
- const target = parsedTargetsArray[i];
119
- if (!target[consts.isRegisteredTargetSymbol]) {
120
- target[consts.isRegisteredTargetSymbol] = true;
121
- const isSvgType = helpers.isSvg(target);
122
- const isDom = /** @type {DOMTarget} */(target).nodeType || isSvgType;
123
- if (isDom) {
124
- target[consts.isDomSymbol] = true;
125
- target[consts.isSvgSymbol] = isSvgType;
126
- target[consts.transformsSymbol] = {};
127
- }
116
+ for (let i = 0; i < parsedTargetsLength; i++) {
117
+ const target = parsedTargetsArray[i];
118
+ if (!target[consts.isRegisteredTargetSymbol]) {
119
+ target[consts.isRegisteredTargetSymbol] = true;
120
+ const isSvgType = helpers.isSvg(target);
121
+ const isDom = /** @type {DOMTarget} */(target).nodeType || isSvgType;
122
+ if (isDom) {
123
+ target[consts.isDomSymbol] = true;
124
+ target[consts.isSvgSymbol] = isSvgType;
125
+ target[consts.transformsSymbol] = {};
128
126
  }
129
127
  }
130
128
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -111,18 +111,16 @@ function parseTargets(targets) {
111
111
  function registerTargets(targets) {
112
112
  const parsedTargetsArray = parseTargets(targets);
113
113
  const parsedTargetsLength = parsedTargetsArray.length;
114
- if (parsedTargetsLength) {
115
- for (let i = 0; i < parsedTargetsLength; i++) {
116
- const target = parsedTargetsArray[i];
117
- if (!target[isRegisteredTargetSymbol]) {
118
- target[isRegisteredTargetSymbol] = true;
119
- const isSvgType = isSvg(target);
120
- const isDom = /** @type {DOMTarget} */(target).nodeType || isSvgType;
121
- if (isDom) {
122
- target[isDomSymbol] = true;
123
- target[isSvgSymbol] = isSvgType;
124
- target[transformsSymbol] = {};
125
- }
114
+ for (let i = 0; i < parsedTargetsLength; i++) {
115
+ const target = parsedTargetsArray[i];
116
+ if (!target[isRegisteredTargetSymbol]) {
117
+ target[isRegisteredTargetSymbol] = true;
118
+ const isSvgType = isSvg(target);
119
+ const isDom = /** @type {DOMTarget} */(target).nodeType || isSvgType;
120
+ if (isDom) {
121
+ target[isDomSymbol] = true;
122
+ target[isSvgSymbol] = isSvgType;
123
+ target[transformsSymbol] = {};
126
124
  }
127
125
  }
128
126
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - ESM
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - core - CJS
3
- * @version v4.4.1
3
+ * @version v4.5.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -10,6 +10,7 @@
10
10
  var consts = require('./consts.cjs');
11
11
  var helpers = require('./helpers.cjs');
12
12
  var transforms = require('./transforms.cjs');
13
+ var registry = require('../adapters/registry.cjs');
13
14
  var colors = require('./colors.cjs');
14
15
 
15
16
  /**
@@ -33,6 +34,21 @@ const setValue = (targetValue, defaultValue) => {
33
34
  return helpers.isUnd(targetValue) ? defaultValue : targetValue;
34
35
  };
35
36
 
37
+ /**
38
+ * 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.
39
+ *
40
+ * @param {String} value
41
+ * @param {Target} target
42
+ * @return {String|Number}
43
+ */
44
+ const resolveCssVar = (value, target) => {
45
+ const match = value.match(consts.cssVariableMatchRgx);
46
+ const el = target[consts.isDomSymbol] ? target : document.documentElement;
47
+ let computed = getComputedStyle(/** @type {HTMLElement} */(el))?.getPropertyValue(match[1]);
48
+ if ((!computed || computed.trim() === consts.emptyString) && match[2]) computed = match[2].trim();
49
+ return computed || 0;
50
+ };
51
+
36
52
  /**
37
53
  * @param {TweenPropValue} value
38
54
  * @param {Target} target
@@ -43,30 +59,26 @@ const setValue = (targetValue, defaultValue) => {
43
59
  * @return {any}
44
60
  */
45
61
  const getFunctionValue = (value, target, index, targets, store, prevTween) => {
46
- let func;
47
62
  if (helpers.isFnc(value)) {
48
- func = () => {
63
+ if (!store) {
64
+ const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
65
+ // Fallback to 0 if the function returns undefined, NaN, null, false or 0
66
+ return !isNaN(+computed) ? +computed : computed || 0;
67
+ }
68
+ const func = () => {
49
69
  const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
50
- // Fallback to 0 if the function returns undefined / NaN / null / false / 0
51
70
  return !isNaN(+computed) ? +computed : computed || 0;
52
71
  };
53
- } else if (helpers.isStr(value) && helpers.stringStartsWith(value, consts.cssVarPrefix)) {
54
- func = () => {
55
- const match = value.match(consts.cssVariableMatchRgx);
56
- const cssVarName = match[1];
57
- const fallbackValue = match[2];
58
- let computed = getComputedStyle(/** @type {HTMLElement} */(target))?.getPropertyValue(cssVarName);
59
- // Use fallback if CSS variable is not set or empty
60
- if ((!computed || computed.trim() === consts.emptyString) && fallbackValue) {
61
- computed = fallbackValue.trim();
62
- }
63
- return computed || 0;
64
- };
65
- } else {
66
- return value;
72
+ store.func = func;
73
+ return func();
67
74
  }
68
- if (store) store.func = func;
69
- return func();
75
+ if (helpers.isStr(value) && helpers.stringStartsWith(value, consts.cssVarPrefix)) {
76
+ if (!store) return resolveCssVar(/** @type {String} */(value), target);
77
+ const func = () => resolveCssVar(/** @type {String} */(value), target);
78
+ store.func = func;
79
+ return func();
80
+ }
81
+ return value;
70
82
  };
71
83
 
72
84
  /**
@@ -113,6 +125,12 @@ const getCSSValue = (target, propName, animationInlineStyles) => {
113
125
  */
114
126
  const getOriginalAnimatableValue = (target, propName, tweenType, animationInlineStyles) => {
115
127
  const type = !helpers.isUnd(tweenType) ? tweenType : getTweenType(target, propName);
128
+ const adapterProp = registry.resolveAdapterEntry(target, propName);
129
+ if (adapterProp) {
130
+ const value = adapterProp.get(target);
131
+ if (value && animationInlineStyles) animationInlineStyles[propName] = value;
132
+ return value == null ? 0 : value;
133
+ }
116
134
  if (type === consts.tweenTypes.OBJECT) {
117
135
  const value = target[propName];
118
136
  if (value && animationInlineStyles) animationInlineStyles[propName] = value;
@@ -154,7 +172,7 @@ const createDecomposedValueTargetObject = () => {
154
172
  };
155
173
 
156
174
  /**
157
- * @param {String|Number} rawValue
175
+ * @param {String|Number|Object} rawValue
158
176
  * @param {TweenDecomposedValue} targetObject
159
177
  * @return {TweenDecomposedValue}
160
178
  */
@@ -172,39 +190,38 @@ const decomposeRawValue = (rawValue, targetObject) => {
172
190
  // It's a number
173
191
  targetObject.n = num;
174
192
  return targetObject;
193
+ }
194
+ // let str = /** @type {String} */(rawValue).trim();
195
+ let str = /** @type {String} */(rawValue);
196
+ // Parsing operators (+=, -=, *=) manually is much faster than using regex here
197
+ if (str[1] === '=') {
198
+ targetObject.o = str[0];
199
+ str = str.slice(2);
200
+ }
201
+ // Skip exec regex if the value type is complex or color to avoid long regex backtracking
202
+ const unitMatch = str.includes(' ') ? false : consts.unitsExecRgx.exec(str);
203
+ if (unitMatch) {
204
+ // Has a number and a unit
205
+ targetObject.t = consts.valueTypes.UNIT;
206
+ targetObject.n = +unitMatch[1];
207
+ targetObject.u = unitMatch[2];
208
+ return targetObject;
209
+ } else if (targetObject.o) {
210
+ // Has an operator (+=, -=, *=)
211
+ targetObject.n = +str;
212
+ return targetObject;
213
+ } else if (helpers.isCol(str)) {
214
+ // Color string
215
+ targetObject.t = consts.valueTypes.COLOR;
216
+ targetObject.d = colors.convertColorStringValuesToRgbaArray(str);
217
+ return targetObject;
175
218
  } else {
176
- // let str = /** @type {String} */(rawValue).trim();
177
- let str = /** @type {String} */(rawValue);
178
- // Parsing operators (+=, -=, *=) manually is much faster than using regex here
179
- if (str[1] === '=') {
180
- targetObject.o = str[0];
181
- str = str.slice(2);
182
- }
183
- // Skip exec regex if the value type is complex or color to avoid long regex backtracking
184
- const unitMatch = str.includes(' ') ? false : consts.unitsExecRgx.exec(str);
185
- if (unitMatch) {
186
- // Has a number and a unit
187
- targetObject.t = consts.valueTypes.UNIT;
188
- targetObject.n = +unitMatch[1];
189
- targetObject.u = unitMatch[2];
190
- return targetObject;
191
- } else if (targetObject.o) {
192
- // Has an operator (+=, -=, *=)
193
- targetObject.n = +str;
194
- return targetObject;
195
- } else if (helpers.isCol(str)) {
196
- // Is a color
197
- targetObject.t = consts.valueTypes.COLOR;
198
- targetObject.d = colors.convertColorStringValuesToRgbaArray(str);
199
- return targetObject;
200
- } else {
201
- // Is a more complex string (generally svg coords, calc() or filters CSS values)
202
- const matchedNumbers = str.match(consts.digitWithExponentRgx);
203
- targetObject.t = consts.valueTypes.COMPLEX;
204
- targetObject.d = matchedNumbers ? matchedNumbers.map(Number) : [];
205
- targetObject.s = str.split(consts.digitWithExponentRgx) || [];
206
- return targetObject;
207
- }
219
+ // Is a more complex string (generally svg coords, calc() or filters CSS values)
220
+ const matchedNumbers = str.match(consts.digitWithExponentRgx);
221
+ targetObject.t = consts.valueTypes.COMPLEX;
222
+ targetObject.d = matchedNumbers ? matchedNumbers.map(Number) : [];
223
+ targetObject.s = str.split(consts.digitWithExponentRgx) || [];
224
+ return targetObject;
208
225
  }
209
226
  };
210
227
 
@@ -225,30 +242,6 @@ const decomposeTweenValue = (tween, targetObject) => {
225
242
 
226
243
  const decomposedOriginalValue = createDecomposedValueTargetObject();
227
244
 
228
- /**
229
- * @param {Tween} tween
230
- * @param {Number} progress
231
- * @param {Number} precision
232
- * @return {String}
233
- */
234
- const composeColorValue = (tween, progress, precision) => {
235
- const mod = tween._modifier;
236
- const fn = tween._fromNumbers;
237
- const tn = tween._toNumbers;
238
- const r = helpers.round(helpers.clamp(/** @type {Number} */(mod(helpers.lerp(fn[0], tn[0], progress))), 0, 255), 0);
239
- const g = helpers.round(helpers.clamp(/** @type {Number} */(mod(helpers.lerp(fn[1], tn[1], progress))), 0, 255), 0);
240
- const b = helpers.round(helpers.clamp(/** @type {Number} */(mod(helpers.lerp(fn[2], tn[2], progress))), 0, 255), 0);
241
- const a = helpers.clamp(/** @type {Number} */(mod(helpers.round(helpers.lerp(fn[3], tn[3], progress), precision))), 0, 1);
242
- if (tween._composition !== consts.compositionTypes.none) {
243
- const ns = tween._numbers;
244
- ns[0] = r;
245
- ns[1] = g;
246
- ns[2] = b;
247
- ns[3] = a;
248
- }
249
- return `rgba(${r},${g},${b},${a})`;
250
- };
251
-
252
245
  /**
253
246
  * @param {Tween} tween
254
247
  * @param {Number} progress
@@ -260,20 +253,18 @@ const composeComplexValue = (tween, progress, precision) => {
260
253
  const fn = tween._fromNumbers;
261
254
  const tn = tween._toNumbers;
262
255
  const ts = tween._strings;
263
- const hasComposition = tween._composition !== consts.compositionTypes.none;
264
256
  let v = ts[0];
265
257
  for (let j = 0, l = tn.length; j < l; j++) {
266
258
  const n = /** @type {Number} */(mod(helpers.round(helpers.lerp(fn[j], tn[j], progress), precision)));
267
259
  const s = ts[j + 1];
268
260
  v += `${s ? n + s : n}`;
269
- if (hasComposition) {
270
- tween._numbers[j] = n;
271
- }
261
+ // 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.
262
+ // Potential optimization, skip the write when nothing reads it: if (hasComposition || tween._setter) tween._numbers[j] = n;
263
+ tween._numbers[j] = n;
272
264
  }
273
265
  return v;
274
266
  };
275
267
 
276
- exports.composeColorValue = composeColorValue;
277
268
  exports.composeComplexValue = composeComplexValue;
278
269
  exports.createDecomposedValueTargetObject = createDecomposedValueTargetObject;
279
270
  exports.decomposeRawValue = decomposeRawValue;
@@ -4,10 +4,9 @@ export function getTweenType(target: Target, prop: string): tweenTypes;
4
4
  export function getOriginalAnimatableValue(target: Target, propName: string, tweenType?: tweenTypes, animationInlineStyles?: any | void): string | number;
5
5
  export function getRelativeValue(x: number, y: number, operator: string): number;
6
6
  export function createDecomposedValueTargetObject(): TweenDecomposedValue;
7
- export function decomposeRawValue(rawValue: string | number, targetObject: TweenDecomposedValue): TweenDecomposedValue;
7
+ export function decomposeRawValue(rawValue: string | number | any, targetObject: TweenDecomposedValue): TweenDecomposedValue;
8
8
  export function decomposeTweenValue(tween: Tween, targetObject: TweenDecomposedValue): TweenDecomposedValue;
9
9
  export const decomposedOriginalValue: TweenDecomposedValue;
10
- export function composeColorValue(tween: Tween, progress: number, precision: number): string;
11
10
  export function composeComplexValue(tween: Tween, progress: number, precision: number): string;
12
11
  import type { TweenPropValue } from '../types/index.js';
13
12
  import type { Target } from '../types/index.js';