animejs 4.4.0 → 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.
- package/README.md +4 -5
- package/dist/bundles/anime.esm.js +491 -272
- package/dist/bundles/anime.esm.min.js +2 -2
- package/dist/bundles/anime.umd.js +491 -272
- package/dist/bundles/anime.umd.min.js +2 -2
- package/dist/modules/adapters/index.cjs +14 -0
- package/dist/modules/adapters/index.d.ts +1 -0
- package/dist/modules/adapters/index.js +8 -0
- package/dist/modules/adapters/registry.cjs +149 -0
- package/dist/modules/adapters/registry.d.ts +65 -0
- package/dist/modules/adapters/registry.js +146 -0
- package/dist/modules/adapters/three/adapter.cjs +26 -0
- package/dist/modules/adapters/three/adapter.d.ts +15 -0
- package/dist/modules/adapters/three/adapter.js +24 -0
- package/dist/modules/adapters/three/helpers.cjs +297 -0
- package/dist/modules/adapters/three/helpers.d.ts +89 -0
- package/dist/modules/adapters/three/helpers.js +280 -0
- package/dist/modules/adapters/three/index.cjs +20 -0
- package/dist/modules/adapters/three/index.d.ts +2 -0
- package/dist/modules/adapters/three/index.js +12 -0
- package/dist/modules/adapters/three/instance.cjs +368 -0
- package/dist/modules/adapters/three/instance.d.ts +133 -0
- package/dist/modules/adapters/three/instance.js +365 -0
- package/dist/modules/adapters/three/object3d.cjs +214 -0
- package/dist/modules/adapters/three/object3d.d.ts +1 -0
- package/dist/modules/adapters/three/object3d.js +212 -0
- package/dist/modules/adapters/three/resolvers.cjs +105 -0
- package/dist/modules/adapters/three/resolvers.d.ts +1 -0
- package/dist/modules/adapters/three/resolvers.js +103 -0
- package/dist/modules/adapters/three/uniform.cjs +41 -0
- package/dist/modules/adapters/three/uniform.d.ts +1 -0
- package/dist/modules/adapters/three/uniform.js +39 -0
- package/dist/modules/animatable/animatable.cjs +2 -1
- package/dist/modules/animatable/animatable.d.ts +2 -1
- package/dist/modules/animatable/animatable.js +2 -1
- package/dist/modules/animatable/index.cjs +1 -1
- package/dist/modules/animatable/index.js +1 -1
- package/dist/modules/animation/additive.cjs +1 -1
- package/dist/modules/animation/additive.js +1 -1
- package/dist/modules/animation/animation.cjs +43 -16
- package/dist/modules/animation/animation.d.ts +5 -0
- package/dist/modules/animation/animation.js +45 -18
- package/dist/modules/animation/composition.cjs +38 -35
- package/dist/modules/animation/composition.js +38 -35
- package/dist/modules/animation/index.cjs +1 -1
- package/dist/modules/animation/index.js +1 -1
- package/dist/modules/core/clock.cjs +11 -15
- package/dist/modules/core/clock.d.ts +0 -2
- package/dist/modules/core/clock.js +11 -15
- package/dist/modules/core/colors.cjs +1 -1
- package/dist/modules/core/colors.js +1 -1
- package/dist/modules/core/consts.cjs +15 -1
- package/dist/modules/core/consts.d.ts +2 -0
- package/dist/modules/core/consts.js +14 -2
- package/dist/modules/core/globals.cjs +7 -4
- package/dist/modules/core/globals.d.ts +8 -2
- package/dist/modules/core/globals.js +8 -5
- package/dist/modules/core/helpers.cjs +2 -2
- package/dist/modules/core/helpers.js +3 -3
- package/dist/modules/core/render.cjs +93 -73
- package/dist/modules/core/render.js +96 -76
- package/dist/modules/core/styles.cjs +16 -2
- package/dist/modules/core/styles.js +16 -2
- package/dist/modules/core/targets.cjs +11 -13
- package/dist/modules/core/targets.js +11 -13
- package/dist/modules/core/transforms.cjs +1 -1
- package/dist/modules/core/transforms.js +1 -1
- package/dist/modules/core/units.cjs +1 -1
- package/dist/modules/core/units.js +1 -1
- package/dist/modules/core/values.cjs +73 -82
- package/dist/modules/core/values.d.ts +1 -2
- package/dist/modules/core/values.js +76 -84
- package/dist/modules/draggable/draggable.cjs +1 -1
- package/dist/modules/draggable/draggable.js +1 -1
- package/dist/modules/draggable/index.cjs +1 -1
- package/dist/modules/draggable/index.js +1 -1
- package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
- package/dist/modules/easings/cubic-bezier/index.js +1 -1
- package/dist/modules/easings/eases/index.cjs +1 -1
- package/dist/modules/easings/eases/index.js +1 -1
- package/dist/modules/easings/eases/parser.cjs +3 -3
- package/dist/modules/easings/eases/parser.d.ts +4 -5
- package/dist/modules/easings/eases/parser.js +3 -3
- package/dist/modules/easings/index.cjs +1 -1
- package/dist/modules/easings/index.js +1 -1
- package/dist/modules/easings/irregular/index.cjs +1 -1
- package/dist/modules/easings/irregular/index.js +1 -1
- package/dist/modules/easings/linear/index.cjs +1 -1
- package/dist/modules/easings/linear/index.js +1 -1
- package/dist/modules/easings/none.cjs +1 -1
- package/dist/modules/easings/none.js +1 -1
- package/dist/modules/easings/spring/index.cjs +1 -1
- package/dist/modules/easings/spring/index.js +1 -1
- package/dist/modules/easings/steps/index.cjs +1 -1
- package/dist/modules/easings/steps/index.js +1 -1
- package/dist/modules/engine/engine.cjs +4 -2
- package/dist/modules/engine/engine.js +4 -2
- package/dist/modules/engine/index.cjs +1 -1
- package/dist/modules/engine/index.js +1 -1
- package/dist/modules/events/index.cjs +1 -1
- package/dist/modules/events/index.js +1 -1
- package/dist/modules/events/scroll.cjs +3 -1
- package/dist/modules/events/scroll.js +3 -1
- package/dist/modules/index.cjs +1 -1
- package/dist/modules/index.js +1 -1
- package/dist/modules/layout/index.cjs +1 -1
- package/dist/modules/layout/index.js +1 -1
- package/dist/modules/layout/layout.cjs +1 -1
- package/dist/modules/layout/layout.js +1 -1
- package/dist/modules/scope/index.cjs +1 -1
- package/dist/modules/scope/index.js +1 -1
- package/dist/modules/scope/scope.cjs +1 -1
- package/dist/modules/scope/scope.js +1 -1
- package/dist/modules/svg/drawable.cjs +1 -1
- package/dist/modules/svg/drawable.js +1 -1
- package/dist/modules/svg/helpers.cjs +1 -1
- package/dist/modules/svg/helpers.js +1 -1
- package/dist/modules/svg/index.cjs +1 -1
- package/dist/modules/svg/index.js +1 -1
- package/dist/modules/svg/morphto.cjs +1 -1
- package/dist/modules/svg/morphto.js +1 -1
- package/dist/modules/svg/motionpath.cjs +1 -1
- package/dist/modules/svg/motionpath.js +1 -1
- package/dist/modules/text/index.cjs +1 -1
- package/dist/modules/text/index.js +1 -1
- package/dist/modules/text/scramble.cjs +12 -2
- package/dist/modules/text/scramble.d.ts +9 -1
- package/dist/modules/text/scramble.js +12 -2
- package/dist/modules/text/split.cjs +2 -1
- package/dist/modules/text/split.js +2 -1
- package/dist/modules/timeline/index.cjs +1 -1
- package/dist/modules/timeline/index.js +1 -1
- package/dist/modules/timeline/position.cjs +1 -1
- package/dist/modules/timeline/position.js +1 -1
- package/dist/modules/timeline/timeline.cjs +14 -5
- package/dist/modules/timeline/timeline.d.ts +3 -3
- package/dist/modules/timeline/timeline.js +14 -5
- package/dist/modules/timer/index.cjs +1 -1
- package/dist/modules/timer/index.js +1 -1
- package/dist/modules/timer/timer.cjs +1 -1
- package/dist/modules/timer/timer.js +1 -1
- package/dist/modules/types/index.d.ts +36 -11
- package/dist/modules/utils/chainable.cjs +1 -1
- package/dist/modules/utils/chainable.js +1 -1
- package/dist/modules/utils/index.cjs +1 -1
- package/dist/modules/utils/index.js +1 -1
- package/dist/modules/utils/number.cjs +1 -1
- package/dist/modules/utils/number.js +1 -1
- package/dist/modules/utils/random.cjs +4 -3
- package/dist/modules/utils/random.d.ts +1 -1
- package/dist/modules/utils/random.js +4 -3
- package/dist/modules/utils/stagger.cjs +67 -13
- package/dist/modules/utils/stagger.js +69 -15
- package/dist/modules/utils/target.cjs +4 -1
- package/dist/modules/utils/target.js +4 -1
- package/dist/modules/utils/time.cjs +6 -5
- package/dist/modules/utils/time.d.ts +1 -1
- package/dist/modules/utils/time.js +6 -5
- package/dist/modules/waapi/composition.cjs +1 -1
- package/dist/modules/waapi/composition.js +1 -1
- package/dist/modules/waapi/index.cjs +1 -1
- package/dist/modules/waapi/index.js +1 -1
- package/dist/modules/waapi/waapi.cjs +1 -1
- package/dist/modules/waapi/waapi.js +1 -1
- package/package.json +38 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Anime.js - core - CJS
|
|
3
|
-
* @version v4.
|
|
3
|
+
* @version v4.5.0
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2026 - Julian Garnier
|
|
6
6
|
*/
|
|
@@ -76,12 +76,14 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
76
76
|
// Execute the "expensive" iterations calculations only when necessary
|
|
77
77
|
if (iterationCount > 1) {
|
|
78
78
|
// bitwise NOT operator seems to be generally faster than Math.floor() across browsers
|
|
79
|
-
const
|
|
79
|
+
const period = iterationDuration + (isCurrentTimeEqualOrAboveDuration ? 0 : _loopDelay);
|
|
80
|
+
const currentIteration = ~~(tickableCurrentTime / period);
|
|
80
81
|
tickable._currentIteration = helpers.clamp(currentIteration, 0, iterationCount);
|
|
81
82
|
// Prevent the iteration count to go above the max iterations when reaching the end of the animation
|
|
82
83
|
if (isCurrentTimeEqualOrAboveDuration) tickable._currentIteration--;
|
|
83
84
|
isOdd = tickable._currentIteration % 2;
|
|
84
|
-
|
|
85
|
+
// 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.
|
|
86
|
+
iterationElapsedTime = tickableCurrentTime - currentIteration * period || 0;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
// Checks if exactly one of _reversed and (_alternate && isOdd) is true
|
|
@@ -113,12 +115,14 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
113
115
|
if (
|
|
114
116
|
forcedTick ||
|
|
115
117
|
tickMode === consts.tickModes.AUTO && (
|
|
116
|
-
|
|
118
|
+
// Timeline children render from their offset instead of their delay so the gap left by a truncated sibling is covered on seek.
|
|
119
|
+
time >= (parent && tickableDelay > 0 ? 0 : tickableDelay) && time <= tickableEndTime || // Normal render
|
|
117
120
|
time <= tickableDelay && tickablePrevTime > tickableDelay || // Playhead is before the animation start time so make sure the animation is at its initial state
|
|
118
121
|
time >= tickableEndTime && tickablePrevTime !== duration // Playhead is after the animation end time so make sure the animation is at its end state
|
|
119
122
|
) ||
|
|
120
123
|
iterationTime >= tickableEndTime && tickablePrevTime !== duration ||
|
|
121
|
-
iterationTime
|
|
124
|
+
// 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.
|
|
125
|
+
iterationTime <= tickableDelay && tickablePrevTime > 0 && !isCurrentTimeEqualOrAboveDuration ||
|
|
122
126
|
time <= tickablePrevTime && tickablePrevTime === duration && completed || // Force a render if a seek occurs on an completed animation
|
|
123
127
|
isCurrentTimeEqualOrAboveDuration && !completed && isSetter // This prevents 0 duration tickables to be skipped
|
|
124
128
|
) {
|
|
@@ -134,7 +138,8 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
134
138
|
|
|
135
139
|
// Time has jumped more than globals.tickThreshold so consider this tick manual
|
|
136
140
|
const forcedRender = forcedTick || (isRunningBackwards ? deltaTime * -1 : deltaTime) >= globals.globals.tickThreshold;
|
|
137
|
-
|
|
141
|
+
// Round to match the precision of tween._absoluteStartTime so equal-time boundary checks compare cleanly without floating point drift from the unrounded _offset.
|
|
142
|
+
const absoluteTime = helpers.round(tickable._offset + (parent ? parent._offset : 0) + tickableDelay + iterationTime, 12);
|
|
138
143
|
|
|
139
144
|
// Only Animation can have tweens, Timer returns undefined
|
|
140
145
|
let tween = /** @type {Tween} */(/** @type {JSAnimation} */(tickable)._head);
|
|
@@ -153,15 +158,38 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
153
158
|
const tweenNextRep = tween._nextRep;
|
|
154
159
|
const tweenPrevRep = tween._prevRep;
|
|
155
160
|
const tweenHasComposition = tweenComposition !== consts.compositionTypes.none;
|
|
161
|
+
// The previous sibling stops writing at its truncated end, so this tween takes over the hold from that point.
|
|
162
|
+
const tweenPrevRepEndTime = tweenPrevRep ? tweenPrevRep._absoluteStartTime + tweenPrevRep._changeDuration : 0;
|
|
163
|
+
const tweenPrevRepIsCrossParent = tweenPrevRep && tweenPrevRep.parent !== tween.parent;
|
|
164
|
+
// Same parent keyframes take over at their own start, end plus delay equals the next start by construction.
|
|
165
|
+
// Cross parent siblings take over at their update start.
|
|
166
|
+
// Negative delay siblings take over at their own start instead.
|
|
167
|
+
const tweenNextRepTakeover = !tweenNextRep || tweenNextRep._isOverridden ? tweenAbsEndTime :
|
|
168
|
+
tweenNextRep.parent === tween.parent ? tweenAbsEndTime + tweenNextRep._delay :
|
|
169
|
+
tweenNextRep._absoluteStartTime < tweenNextRep._absoluteUpdateStartTime ? tweenNextRep._absoluteStartTime : tweenNextRep._absoluteUpdateStartTime;
|
|
156
170
|
|
|
157
171
|
if ((forcedRender || (
|
|
158
|
-
|
|
159
|
-
(tweenCurrentTime !==
|
|
160
|
-
|
|
172
|
+
// 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.
|
|
173
|
+
(tweenCurrentTime !== tweenChangeDuration || absoluteTime <= tweenNextRepTakeover ||
|
|
174
|
+
(tweenPrevRep && !tweenPrevRepIsCrossParent && (!tweenNextRep || tweenNextRep.parent !== tween.parent))) &&
|
|
175
|
+
// A cross parent tween re-renders its from value from the previous sibling truncated end so the handoff gap holds.
|
|
176
|
+
// 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.
|
|
177
|
+
(tweenCurrentTime !== 0 || absoluteTime >= tween._absoluteStartTime ||
|
|
178
|
+
(tweenPrevRepIsCrossParent && !tween._hasFromValue && !tweenPrevRep._isOverridden && absoluteTime >= tweenPrevRepEndTime) ||
|
|
179
|
+
(tweenNextRep && !tweenNextRep._isOverridden && tweenNextRep.parent === tween.parent && tweenNextRep._currentTime !== 0 && iterationTime < tweenNextRep._startTime))
|
|
180
|
+
)) &&
|
|
181
|
+
// 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.
|
|
182
|
+
(!tweenPrevRep || tweenPrevRepIsCrossParent || iterationTime >= tween._startTime) &&
|
|
183
|
+
(!tweenHasComposition || (
|
|
161
184
|
!tween._isOverridden &&
|
|
162
185
|
(!tween._isOverlapped || absoluteTime <= tweenAbsEndTime) &&
|
|
163
|
-
|
|
164
|
-
(!
|
|
186
|
+
// The next sibling owns the value past its takeover point, so yielding there keeps writes single owner in both directions.
|
|
187
|
+
(!tweenNextRep || tweenNextRep._isOverridden || absoluteTime <= tweenNextRepTakeover) &&
|
|
188
|
+
// The previous sibling owns the value up to its truncated end.
|
|
189
|
+
// Cross parent tweens take over the hold from that point, explicit from values wait for their own start.
|
|
190
|
+
(!tweenPrevRep || (tweenPrevRep._isOverridden || (!tweenPrevRepIsCrossParent ?
|
|
191
|
+
absoluteTime >= tweenPrevRepEndTime + tween._delay :
|
|
192
|
+
absoluteTime >= tween._absoluteStartTime || (!tween._hasFromValue && absoluteTime >= tweenPrevRepEndTime))))
|
|
165
193
|
))
|
|
166
194
|
) {
|
|
167
195
|
|
|
@@ -172,7 +200,7 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
172
200
|
const tweenType = tween._tweenType;
|
|
173
201
|
const tweenIsObject = tweenType === consts.tweenTypes.OBJECT;
|
|
174
202
|
const tweenIsNumber = tweenValueType === consts.valueTypes.NUMBER;
|
|
175
|
-
// Only round the in-between frames values if the final value is a string
|
|
203
|
+
// 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.
|
|
176
204
|
const tweenPrecision = (tweenIsNumber && tweenIsObject) || tweenProgress === 0 || tweenProgress === 1 ? -1 : globals.globals.precision;
|
|
177
205
|
|
|
178
206
|
// Recompose tween value
|
|
@@ -188,7 +216,22 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
188
216
|
number = /** @type {Number} */(tweenModifier(helpers.round(helpers.lerp(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision)));
|
|
189
217
|
value = `${number}${tween._unit}`;
|
|
190
218
|
} else if (tweenValueType === consts.valueTypes.COLOR) {
|
|
191
|
-
|
|
219
|
+
const ns = tween._numbers;
|
|
220
|
+
const fn = tween._fromNumbers;
|
|
221
|
+
const tn = tween._toNumbers;
|
|
222
|
+
const omt = 1 - tweenProgress;
|
|
223
|
+
const fr = fn[0], fg = fn[1], fb = fn[2];
|
|
224
|
+
const tr = tn[0], tg = tn[1], tb = tn[2];
|
|
225
|
+
// RGB channels lerp in pseudo-linear space (square inputs, sqrt result) to approximate gamma-correct blending.
|
|
226
|
+
// See https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear.
|
|
227
|
+
ns[0] = /** @type {Number} */(tweenModifier(Math.sqrt(fr * fr * omt + tr * tr * tweenProgress)));
|
|
228
|
+
ns[1] = /** @type {Number} */(tweenModifier(Math.sqrt(fg * fg * omt + tg * tg * tweenProgress)));
|
|
229
|
+
ns[2] = /** @type {Number} */(tweenModifier(Math.sqrt(fb * fb * omt + tb * tb * tweenProgress)));
|
|
230
|
+
ns[3] = /** @type {Number} */(tweenModifier(helpers.lerp(fn[3], tn[3], tweenProgress)));
|
|
231
|
+
// The rgba string is built only for the dispatch path or the internalRender composition tick (setters handles the color comp)
|
|
232
|
+
if (!tween._setter || internalRender) {
|
|
233
|
+
value = `rgba(${helpers.round(ns[0], 0)},${helpers.round(ns[1], 0)},${helpers.round(ns[2], 0)},${ns[3]})`;
|
|
234
|
+
}
|
|
192
235
|
} else if (tweenValueType === consts.valueTypes.COMPLEX) {
|
|
193
236
|
value = values.composeComplexValue(tween, tweenProgress, tweenPrecision);
|
|
194
237
|
}
|
|
@@ -203,7 +246,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
203
246
|
const tweenProperty = tween.property;
|
|
204
247
|
tweenTarget = tween.target;
|
|
205
248
|
|
|
206
|
-
if (
|
|
249
|
+
if (tween._setter) {
|
|
250
|
+
tween._setter(tweenTarget, number, tween);
|
|
251
|
+
} else if (tweenIsObject) {
|
|
207
252
|
tweenTarget[tweenProperty] = value;
|
|
208
253
|
} else if (tweenType === consts.tweenTypes.ATTRIBUTE) {
|
|
209
254
|
/** @type {DOMTarget} */(tweenTarget).setAttribute(tweenProperty, /** @type {String} */(value));
|
|
@@ -231,6 +276,9 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
231
276
|
tween._value = value;
|
|
232
277
|
}
|
|
233
278
|
|
|
279
|
+
} else if (tweenCurrentTime && tweenPrevRep && !tweenPrevRepIsCrossParent && iterationTime < tween._startTime) {
|
|
280
|
+
// Mark the keyframe as reverted when the playhead moves before its start, the previous keyframe owns the from revert and writes it once.
|
|
281
|
+
tween._currentTime = 0;
|
|
234
282
|
}
|
|
235
283
|
|
|
236
284
|
if (tweenTransformsNeedUpdate && tween._renderTransforms) {
|
|
@@ -291,50 +339,6 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
291
339
|
return hasRendered;
|
|
292
340
|
};
|
|
293
341
|
|
|
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
|
-
|
|
338
342
|
/**
|
|
339
343
|
* @param {Tickable} tickable
|
|
340
344
|
* @param {Number} time
|
|
@@ -351,28 +355,44 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
351
355
|
const tlIsRunningBackwards = tl.backwards;
|
|
352
356
|
const tlChildrenTime = internalRender ? time : tl._iterationTime;
|
|
353
357
|
const tlCildrenTickTime = helpers.now();
|
|
358
|
+
|
|
354
359
|
let tlChildrenHasRendered = 0;
|
|
355
360
|
let tlChildrenHaveCompleted = true;
|
|
361
|
+
|
|
356
362
|
// If the timeline has looped forward, we need to manually triggers children skipped callbacks
|
|
357
363
|
if (!internalRender && tl._currentIteration !== _currentIteration) {
|
|
358
364
|
const tlIterationDuration = tl.iterationDuration;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
helpers.forEachChildren(tl, (/** @type {JSAnimation} */child) => {
|
|
366
|
+
if (!tlIsRunningBackwards) {
|
|
367
|
+
// Force an internal render to trigger the callbacks if the child has not completed on loop
|
|
368
|
+
if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
|
|
369
|
+
render(child, tlIterationDuration, muteCallbacks, 1, consts.tickModes.FORCE);
|
|
370
|
+
}
|
|
371
|
+
// Reset their began and completed flags to allow retrigering callbacks on the next iteration
|
|
372
|
+
child.began = false;
|
|
373
|
+
child.completed = false;
|
|
374
|
+
} else {
|
|
375
|
+
const childDuration = child.duration;
|
|
376
|
+
const childStartTime = child._offset + child._delay;
|
|
377
|
+
const childEndTime = childStartTime + childDuration;
|
|
378
|
+
// Triggers the onComplete callback on reverse for children on the edges of the timeline
|
|
379
|
+
if (!muteCallbacks && childDuration <= consts.minValue && (!childStartTime || childEndTime === tlIterationDuration)) {
|
|
380
|
+
child.onComplete(child);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
363
384
|
if (!muteCallbacks) tl.onLoop(/** @type {CallbackArgument} */(tl));
|
|
364
385
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
tlChildrenHaveCompleted = renderCtxChildrenHaveCompleted;
|
|
386
|
+
|
|
387
|
+
helpers.forEachChildren(tl, (/** @type {JSAnimation} */child) => {
|
|
388
|
+
const childTime = helpers.round((tlChildrenTime - child._offset) * child._speed, 12); // Rounding is needed when using seconds
|
|
389
|
+
// 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.
|
|
390
|
+
if (tlIsRunningBackwards && childTime > child._delay + child.duration) return;
|
|
391
|
+
const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
|
|
392
|
+
tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
|
|
393
|
+
if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
|
|
394
|
+
}, tlIsRunningBackwards);
|
|
395
|
+
|
|
376
396
|
// Renders on timeline are triggered by its children so it needs to be set after rendering the children
|
|
377
397
|
if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
|
|
378
398
|
// Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Anime.js - core - ESM
|
|
3
|
-
* @version v4.
|
|
3
|
+
* @version v4.5.0
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2026 - Julian Garnier
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { globals } from './globals.js';
|
|
9
|
-
import { tickModes, valueTypes, compositionTypes, tweenTypes, transformsSymbol
|
|
10
|
-
import { forEachChildren,
|
|
9
|
+
import { minValue, tickModes, valueTypes, compositionTypes, tweenTypes, transformsSymbol } from './consts.js';
|
|
10
|
+
import { forEachChildren, round, now, clamp, lerp } from './helpers.js';
|
|
11
11
|
import { buildTransformString } from './transforms.js';
|
|
12
|
-
import {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
(tweenCurrentTime !==
|
|
158
|
-
|
|
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
|
-
|
|
162
|
-
(!
|
|
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
|
-
|
|
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 (
|
|
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) {
|
|
@@ -289,50 +337,6 @@ const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
289
337
|
return hasRendered;
|
|
290
338
|
};
|
|
291
339
|
|
|
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
|
-
|
|
336
340
|
/**
|
|
337
341
|
* @param {Tickable} tickable
|
|
338
342
|
* @param {Number} time
|
|
@@ -349,28 +353,44 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
|
|
|
349
353
|
const tlIsRunningBackwards = tl.backwards;
|
|
350
354
|
const tlChildrenTime = internalRender ? time : tl._iterationTime;
|
|
351
355
|
const tlCildrenTickTime = now();
|
|
356
|
+
|
|
352
357
|
let tlChildrenHasRendered = 0;
|
|
353
358
|
let tlChildrenHaveCompleted = true;
|
|
359
|
+
|
|
354
360
|
// If the timeline has looped forward, we need to manually triggers children skipped callbacks
|
|
355
361
|
if (!internalRender && tl._currentIteration !== _currentIteration) {
|
|
356
362
|
const tlIterationDuration = tl.iterationDuration;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
363
|
+
forEachChildren(tl, (/** @type {JSAnimation} */child) => {
|
|
364
|
+
if (!tlIsRunningBackwards) {
|
|
365
|
+
// Force an internal render to trigger the callbacks if the child has not completed on loop
|
|
366
|
+
if (!child.completed && !child.backwards && child._currentTime < child.iterationDuration) {
|
|
367
|
+
render(child, tlIterationDuration, muteCallbacks, 1, tickModes.FORCE);
|
|
368
|
+
}
|
|
369
|
+
// Reset their began and completed flags to allow retrigering callbacks on the next iteration
|
|
370
|
+
child.began = false;
|
|
371
|
+
child.completed = false;
|
|
372
|
+
} else {
|
|
373
|
+
const childDuration = child.duration;
|
|
374
|
+
const childStartTime = child._offset + child._delay;
|
|
375
|
+
const childEndTime = childStartTime + childDuration;
|
|
376
|
+
// Triggers the onComplete callback on reverse for children on the edges of the timeline
|
|
377
|
+
if (!muteCallbacks && childDuration <= minValue && (!childStartTime || childEndTime === tlIterationDuration)) {
|
|
378
|
+
child.onComplete(child);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
361
382
|
if (!muteCallbacks) tl.onLoop(/** @type {CallbackArgument} */(tl));
|
|
362
383
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
tlChildrenHaveCompleted = renderCtxChildrenHaveCompleted;
|
|
384
|
+
|
|
385
|
+
forEachChildren(tl, (/** @type {JSAnimation} */child) => {
|
|
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;
|
|
389
|
+
const childTickMode = child._fps < tl._fps ? child.requestTick(tlCildrenTickTime) : tickMode;
|
|
390
|
+
tlChildrenHasRendered += render(child, childTime, muteCallbacks, internalRender, childTickMode);
|
|
391
|
+
if (!child.completed && tlChildrenHaveCompleted) tlChildrenHaveCompleted = false;
|
|
392
|
+
}, tlIsRunningBackwards);
|
|
393
|
+
|
|
374
394
|
// Renders on timeline are triggered by its children so it needs to be set after rendering the children
|
|
375
395
|
if (!muteCallbacks && tlChildrenHasRendered) tl.onRender(/** @type {CallbackArgument} */(tl));
|
|
376
396
|
// Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Anime.js - core - CJS
|
|
3
|
-
* @version v4.
|
|
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 (
|
|
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.
|
|
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 (
|
|
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
|
}
|