animejs 4.1.1 → 4.1.3

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/types/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * anime.js - ESM
3
- * @version v4.1.1
3
+ * @version v4.1.3
4
4
  * @author Julian Garnier
5
5
  * @license MIT
6
6
  * @copyright (c) 2025 Julian Garnier
@@ -10,9 +10,9 @@
10
10
  // Environments
11
11
  // TODO: Do we need to check if we're running inside a worker ?
12
12
  const isBrowser = typeof window !== 'undefined';
13
- /** @type {Object|Null} */
14
- const win = isBrowser ? window : null;
15
- /** @type {Document} */
13
+ /** @type {Window & {AnimeJS: Array}|null} */
14
+ const win = isBrowser ? /** @type {Window & {AnimeJS: Array}} */ ( /** @type {unknown} */(window)) : null;
15
+ /** @type {Document|null} */
16
16
  const doc = isBrowser ? document : null;
17
17
  // Enums
18
18
  /** @enum {Number} */
@@ -137,12 +137,12 @@ const globals = {
137
137
  defaults,
138
138
  /** @type {Number} */
139
139
  precision: 4,
140
- /** @type {Number} */
140
+ /** @type {Number} equals 1 in ms mode, 0.001 in s mode */
141
141
  timeScale: 1,
142
142
  /** @type {Number} */
143
143
  tickThreshold: 200,
144
144
  };
145
- const globalVersions = { version: '4.1.1', engine: null };
145
+ const globalVersions = { version: '4.1.3', engine: null };
146
146
  if (isBrowser) {
147
147
  if (!win.AnimeJS)
148
148
  win.AnimeJS = [];
@@ -162,7 +162,6 @@ const toLowerCase = str => str.replace(lowerCaseRgx, '$1-$2').toLowerCase();
162
162
  * @return {Boolean}
163
163
  */
164
164
  const stringStartsWith = (str, sub) => str.indexOf(sub) === 0;
165
- // Time
166
165
  // Note: Date.now is used instead of performance.now since it is precise enough for timings calculations, performs slightly faster and works in Node.js environement.
167
166
  const now = Date.now;
168
167
  // Types checkers
@@ -779,7 +778,7 @@ const tick = (tickable, time, muteCallbacks, internalRender, tickMode) => {
779
778
  if (!muteCallbacks && tlChildrenHasRendered)
780
779
  tl.onRender(/** @type {CallbackArgument} */ (tl));
781
780
  // Triggers the timeline onComplete() once all chindren all completed and the current time has reached the end
782
- if (tlChildrenHaveCompleted && tl._currentTime >= tl.duration) {
781
+ if ((tlChildrenHaveCompleted || tlIsRunningBackwards) && tl._currentTime >= tl.duration) {
783
782
  // Make sure the paused flag is false in case it has been skipped in the render function
784
783
  tl.paused = true;
785
784
  if (!tl.completed) {
@@ -864,9 +863,10 @@ class Engine extends Clock {
864
863
  this.pauseOnDocumentHidden = true;
865
864
  /** @type {DefaultsParams} */
866
865
  this.defaults = defaults;
867
- this.paused = isBrowser && doc.hidden ? true : false;
866
+ // this.paused = isBrowser && doc.hidden ? true : false;
867
+ this.paused = true;
868
868
  /** @type {Number|NodeJS.Immediate} */
869
- this.reqId = null;
869
+ this.reqId = 0;
870
870
  }
871
871
  update() {
872
872
  const time = this._currentTime = now();
@@ -896,12 +896,16 @@ class Engine extends Clock {
896
896
  }
897
897
  }
898
898
  wake() {
899
- if (this.useDefaultMainLoop && !this.reqId && !this.paused) {
899
+ if (this.useDefaultMainLoop && !this.reqId) {
900
+ // Imediatly request a tick to update engine._elapsedTime and get accurate offsetPosition calculation in timer.js
901
+ this.requestTick(now());
900
902
  this.reqId = engineTickMethod(tickEngine);
901
903
  }
902
904
  return this;
903
905
  }
904
906
  pause() {
907
+ if (!this.reqId)
908
+ return;
905
909
  this.paused = true;
906
910
  return killEngine();
907
911
  }
@@ -1033,6 +1037,8 @@ function getNodeList(v) {
1033
1037
  function parseTargets(targets) {
1034
1038
  if (isNil(targets))
1035
1039
  return /** @type {TargetsArray} */ ([]);
1040
+ if (!isBrowser)
1041
+ return /** @type {JSTargetsArray} */ (isArr(targets) && targets.flat(Infinity) || [targets]);
1036
1042
  if (isArr(targets)) {
1037
1043
  const flattened = targets.flat(Infinity);
1038
1044
  /** @type {TargetsArray} */
@@ -1074,8 +1080,6 @@ function parseTargets(targets) {
1074
1080
  }
1075
1081
  return parsed;
1076
1082
  }
1077
- if (!isBrowser)
1078
- return /** @type {JSTargetsArray} */ ([targets]);
1079
1083
  const nodeList = getNodeList(targets);
1080
1084
  if (nodeList)
1081
1085
  return /** @type {DOMTargetsArray} */ (Array.from(nodeList));
@@ -1676,10 +1680,14 @@ const composeTween = (tween, siblings) => {
1676
1680
  if (prevAbsEndTime > absoluteUpdateStartTime) {
1677
1681
  const prevChangeStartTime = prevSibling._startTime;
1678
1682
  const prevTLOffset = prevAbsEndTime - (prevChangeStartTime + prevSibling._updateDuration);
1679
- prevSibling._changeDuration = absoluteUpdateStartTime - prevTLOffset - prevChangeStartTime;
1680
- prevSibling._currentTime = prevSibling._changeDuration;
1683
+ // Rounding is necessary here to minimize floating point errors when working in seconds
1684
+ const updatedPrevChangeDuration = round(absoluteUpdateStartTime - prevTLOffset - prevChangeStartTime, 12);
1685
+ prevSibling._changeDuration = updatedPrevChangeDuration;
1686
+ prevSibling._currentTime = updatedPrevChangeDuration;
1681
1687
  prevSibling._isOverlapped = 1;
1682
- if (prevSibling._changeDuration < minValue) {
1688
+ // Override the previous tween if its new _changeDuration is lower than minValue
1689
+ // TODO: See if it's even neceseeary to test against minValue, checking for 0 might be enough
1690
+ if (updatedPrevChangeDuration < minValue) {
1683
1691
  overrideTween(prevSibling);
1684
1692
  }
1685
1693
  }
@@ -1709,7 +1717,7 @@ const composeTween = (tween, siblings) => {
1709
1717
  prevParent.cancel();
1710
1718
  // Previously, calling .cancel() on a timeline child would affect the render order of other children
1711
1719
  // Worked around this by marking it as .completed and using .pause() for safe removal in the engine loop
1712
- // This is no longer needed since timeline tween composition is now handled separatly
1720
+ // This is no longer needed since timeline tween composition is now handled separately
1713
1721
  // Keeping this here for reference
1714
1722
  // prevParent.completed = true;
1715
1723
  // prevParent.pause();
@@ -1873,13 +1881,12 @@ class Timer extends Clock {
1873
1881
  offsetPosition = parentPosition;
1874
1882
  }
1875
1883
  else {
1876
- let startTime = now();
1877
- // Make sure to tick the engine once if suspended to avoid big gaps with the following offsetPosition calculation
1878
- if (engine.paused) {
1879
- engine.requestTick(startTime);
1880
- startTime = engine._elapsedTime;
1881
- }
1882
- offsetPosition = startTime - engine._startTime;
1884
+ // Make sure to tick the engine once if not currently running to get up to date engine._elapsedTime
1885
+ // to avoid big gaps with the following offsetPosition calculation
1886
+ if (!engine.reqId)
1887
+ engine.requestTick(now());
1888
+ // Make sure to scale the offset position with globals.timeScale to properly handle seconds unit
1889
+ offsetPosition = (engine._elapsedTime - engine._startTime) * globals.timeScale;
1883
1890
  }
1884
1891
  // Timer's parameters
1885
1892
  this.id = !isUnd(id) ? id : ++timerId;
@@ -2062,6 +2069,9 @@ class Timer extends Clock {
2062
2069
  /** @return {this} */
2063
2070
  resetTime() {
2064
2071
  const timeScale = 1 / (this._speed * engine._speed);
2072
+ // TODO: See if we can safely use engine._elapsedTime here
2073
+ // if (!engine.reqId) engine.requestTick(now())
2074
+ // this._startTime = engine._elapsedTime - (this._currentTime + this._delay) * timeScale;
2065
2075
  this._startTime = now() - (this._currentTime + this._delay) * timeScale;
2066
2076
  return this;
2067
2077
  }
@@ -2256,10 +2266,10 @@ const binarySubdivide = (aX, mX1, mX2) => {
2256
2266
  return currentT;
2257
2267
  };
2258
2268
  /**
2259
- * @param {Number} [mX1]
2260
- * @param {Number} [mY1]
2261
- * @param {Number} [mX2]
2262
- * @param {Number} [mY2]
2269
+ * @param {Number} [mX1] The x coordinate of the first point
2270
+ * @param {Number} [mY1] The y coordinate of the first point
2271
+ * @param {Number} [mX2] The x coordinate of the second point
2272
+ * @param {Number} [mY2] The y coordinate of the second point
2263
2273
  * @return {EasingFunction}
2264
2274
  */
2265
2275
  const cubicBezier = (mX1 = 0.5, mY1 = 0.0, mX2 = 0.5, mY2 = 1.0) => (mX1 === mY1 && mX2 === mY2) ? none :
@@ -2279,7 +2289,7 @@ const steps = (steps = 10, fromStart) => {
2279
2289
  /**
2280
2290
  * Without parameters, the linear function creates a non-eased transition.
2281
2291
  * Parameters, if used, creates a piecewise linear easing by interpolating linearly between the specified points.
2282
- * @param {...String|Number} [args] - Points
2292
+ * @param {...(String|Number)} args - Points
2283
2293
  * @return {EasingFunction}
2284
2294
  */
2285
2295
  const linear = (...args) => {
@@ -2875,7 +2885,8 @@ class JSAnimation extends Timer {
2875
2885
  const isFromToArray = isArr(tweenToValue);
2876
2886
  const isFromToValue = isFromToArray || (hasFromvalue && hasToValue);
2877
2887
  const tweenStartTime = prevTween ? lastTweenChangeEndTime + tweenDelay : tweenDelay;
2878
- const absoluteStartTime = absoluteOffsetTime + tweenStartTime;
2888
+ // Rounding is necessary here to minimize floating point errors when working in seconds
2889
+ const absoluteStartTime = round(absoluteOffsetTime + tweenStartTime, 12);
2879
2890
  // Force a onRender callback if the animation contains at least one from value and autoplay is set to false
2880
2891
  if (!shouldTriggerRender && (hasFromvalue || isFromToArray))
2881
2892
  shouldTriggerRender = 1;
@@ -2992,7 +3003,7 @@ class JSAnimation extends Timer {
2992
3003
  shortestValue.s = cloneArray(longestValue.s);
2993
3004
  }
2994
3005
  // Tween factory
2995
- // Rounding is necessary here to minimize floating point errors
3006
+ // Rounding is necessary here to minimize floating point errors when working in seconds
2996
3007
  const tweenUpdateDuration = round(+tweenDuration || minValue, 12);
2997
3008
  /** @type {Tween} */
2998
3009
  const tween = {
@@ -3038,7 +3049,7 @@ class JSAnimation extends Timer {
3038
3049
  if (isNaN(firstTweenChangeStartTime)) {
3039
3050
  firstTweenChangeStartTime = tween._startTime;
3040
3051
  }
3041
- // Rounding is necessary here to minimize floating point errors
3052
+ // Rounding is necessary here to minimize floating point errors when working in seconds
3042
3053
  lastTweenChangeEndTime = round(tweenStartTime + tweenUpdateDuration, 12);
3043
3054
  prevTween = tween;
3044
3055
  animationAnimationLength++;
@@ -3152,7 +3163,8 @@ class JSAnimation extends Timer {
3152
3163
  tween._fromNumber = decomposedOriginalValue.n;
3153
3164
  tween._toNumbers = cloneArray(toTargetObject.d);
3154
3165
  tween._strings = cloneArray(toTargetObject.s);
3155
- tween._toNumber = toTargetObject.n;
3166
+ // Make sure to apply relative operators https://github.com/juliangarnier/anime/issues/1025
3167
+ tween._toNumber = toTargetObject.o ? getRelativeValue(decomposedOriginalValue.n, toTargetObject.n, toTargetObject.o) : toTargetObject.n;
3156
3168
  }
3157
3169
  });
3158
3170
  return this;
@@ -3201,7 +3213,7 @@ const WAAPIEasesLookups = {
3201
3213
  const WAAPIeases = /*#__PURE__*/ (() => {
3202
3214
  const list = {};
3203
3215
  for (let type in easeTypes)
3204
- list[type] = a => easeTypes[type](easeInPower(a));
3216
+ list[type] = (/** @type {String|Number} */ p) => easeTypes[type](easeInPower(p));
3205
3217
  return /** @type {Record<String, EasingFunction>} */ (list);
3206
3218
  })();
3207
3219
  /**
@@ -3240,47 +3252,6 @@ const parseWAAPIEasing = (ease) => {
3240
3252
  }
3241
3253
  return parsedEase;
3242
3254
  };
3243
- /**
3244
- * @typedef {String|Number|Array<String>|Array<Number>} WAAPITweenValue
3245
- */
3246
- /**
3247
- * @callback WAAPIFunctionvalue
3248
- * @param {DOMTarget} target - The animated target
3249
- * @param {Number} index - The target index
3250
- * @param {Number} length - The total number of animated targets
3251
- * @return {WAAPITweenValue}
3252
- */
3253
- /**
3254
- * @typedef {WAAPITweenValue|WAAPIFunctionvalue|Array<String|Number|WAAPIFunctionvalue>} WAAPIKeyframeValue
3255
- */
3256
- /**
3257
- * @typedef {(animation: WAAPIAnimation) => void} WAAPICallback
3258
- */
3259
- /**
3260
- * @typedef {Object} WAAPITweenOptions
3261
- * @property {WAAPIKeyframeValue} [to]
3262
- * @property {WAAPIKeyframeValue} [from]
3263
- * @property {Number|WAAPIFunctionvalue} [duration]
3264
- * @property {Number|WAAPIFunctionvalue} [delay]
3265
- * @property {EasingParam} [ease]
3266
- * @property {CompositeOperation} [composition]
3267
- */
3268
- /**
3269
- * @typedef {Object} WAAPIAnimationOptions
3270
- * @property {Number|Boolean} [loop]
3271
- * @property {Boolean} [Reversed]
3272
- * @property {Boolean} [Alternate]
3273
- * @property {Boolean|ScrollObserver} [autoplay]
3274
- * @property {Number} [playbackRate]
3275
- * @property {Number|WAAPIFunctionvalue} [duration]
3276
- * @property {Number|WAAPIFunctionvalue} [delay]
3277
- * @property {EasingParam} [ease]
3278
- * @property {CompositeOperation} [composition]
3279
- * @property {WAAPICallback} [onComplete]
3280
- */
3281
- /**
3282
- * @typedef {Record<String, WAAPIKeyframeValue | WAAPIAnimationOptions | Boolean | ScrollObserver | WAAPICallback | EasingParam | WAAPITweenOptions> & WAAPIAnimationOptions} WAAPIAnimationParams
3283
- */
3284
3255
  const transformsShorthands = ['x', 'y', 'z'];
3285
3256
  const commonDefaultPXProperties = [
3286
3257
  'perspective',
@@ -3574,7 +3545,7 @@ class WAAPIAnimation {
3574
3545
  * @return {this}
3575
3546
  */
3576
3547
  forEach(callback) {
3577
- const cb = isStr(callback) ? a => a[callback]() : callback;
3548
+ const cb = isStr(callback) ? (/** @type {globalThis.Animation} */ a) => a[callback]() : callback;
3578
3549
  this.animations.forEach(cb);
3579
3550
  return this;
3580
3551
  }
@@ -4075,9 +4046,6 @@ const utils = {
4075
4046
  };
4076
4047
 
4077
4048
 
4078
- /**
4079
- * @typedef {Number|String|Function} TimePosition
4080
- */
4081
4049
  /**
4082
4050
  * Timeline's children offsets positions parser
4083
4051
  * @param {Timeline} timeline
@@ -4094,7 +4062,7 @@ const getPrevChildOffset = (timeline, timePosition) => {
4094
4062
  };
4095
4063
  /**
4096
4064
  * @param {Timeline} timeline
4097
- * @param {TimePosition} [timePosition]
4065
+ * @param {TimelinePosition} [timePosition]
4098
4066
  * @return {Number}
4099
4067
  */
4100
4068
  const parseTimelinePosition = (timeline, timePosition) => {
@@ -4200,17 +4168,17 @@ class Timeline extends Timer {
4200
4168
  * @overload
4201
4169
  * @param {TargetsParam} a1
4202
4170
  * @param {AnimationParams} a2
4203
- * @param {TimePosition} [a3]
4171
+ * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4204
4172
  * @return {this}
4205
4173
  *
4206
4174
  * @overload
4207
4175
  * @param {TimerParams} a1
4208
- * @param {TimePosition} [a2]
4176
+ * @param {TimelinePosition} [a2]
4209
4177
  * @return {this}
4210
4178
  *
4211
4179
  * @param {TargetsParam|TimerParams} a1
4212
- * @param {AnimationParams|TimePosition} a2
4213
- * @param {TimePosition} [a3]
4180
+ * @param {TimelinePosition|AnimationParams} a2
4181
+ * @param {TimelinePosition|StaggerFunction<Number|String>} [a3]
4214
4182
  */
4215
4183
  add(a1, a2, a3) {
4216
4184
  const isAnim = isObj(a2);
@@ -4221,7 +4189,7 @@ class Timeline extends Timer {
4221
4189
  const childParams = /** @type {AnimationParams} */ (a2);
4222
4190
  // Check for function for children stagger positions
4223
4191
  if (isFnc(a3)) {
4224
- const staggeredPosition = /** @type {Function} */ (a3);
4192
+ const staggeredPosition = a3;
4225
4193
  const parsedTargetsArray = parseTargets(/** @type {TargetsParam} */ (a1));
4226
4194
  // Store initial duration before adding new children that will change the duration
4227
4195
  const tlDuration = this.duration;
@@ -4230,7 +4198,8 @@ class Timeline extends Timer {
4230
4198
  // Store the original id in order to add specific indexes to the new animations ids
4231
4199
  const id = childParams.id;
4232
4200
  let i = 0;
4233
- const parsedLength = parsedTargetsArray.length;
4201
+ /** @type {Number} */
4202
+ const parsedLength = (parsedTargetsArray.length);
4234
4203
  parsedTargetsArray.forEach((/** @type {Target} */ target) => {
4235
4204
  // Create a new parameter object for each staggered children
4236
4205
  const staggeredChildParams = { ...childParams };
@@ -4239,7 +4208,7 @@ class Timeline extends Timer {
4239
4208
  this.iterationDuration = tlIterationDuration;
4240
4209
  if (!isUnd(id))
4241
4210
  staggeredChildParams.id = id + '-' + i;
4242
- addTlChild(staggeredChildParams, this, staggeredPosition(target, i, parsedLength, this), target, i, parsedLength);
4211
+ addTlChild(staggeredChildParams, this, parseTimelinePosition(this, staggeredPosition(target, i, parsedLength, this)), target, i, parsedLength);
4243
4212
  i++;
4244
4213
  });
4245
4214
  }
@@ -4251,7 +4220,7 @@ class Timeline extends Timer {
4251
4220
  else {
4252
4221
  // It's a Timer
4253
4222
  addTlChild(
4254
- /** @type TimerParams */ (a1), this, parseTimelinePosition(this, /** @type TimePosition */ (a2)));
4223
+ /** @type TimerParams */ (a1), this, parseTimelinePosition(this, a2));
4255
4224
  }
4256
4225
  return this.init(1); // 1 = internalRender
4257
4226
  }
@@ -4259,21 +4228,21 @@ class Timeline extends Timer {
4259
4228
  /**
4260
4229
  * @overload
4261
4230
  * @param {Tickable} [synced]
4262
- * @param {TimePosition} [position]
4231
+ * @param {TimelinePosition} [position]
4263
4232
  * @return {this}
4264
4233
  *
4265
4234
  * @overload
4266
4235
  * @param {globalThis.Animation} [synced]
4267
- * @param {TimePosition} [position]
4236
+ * @param {TimelinePosition} [position]
4268
4237
  * @return {this}
4269
4238
  *
4270
4239
  * @overload
4271
4240
  * @param {WAAPIAnimation} [synced]
4272
- * @param {TimePosition} [position]
4241
+ * @param {TimelinePosition} [position]
4273
4242
  * @return {this}
4274
4243
  *
4275
4244
  * @param {Tickable|WAAPIAnimation|globalThis.Animation} [synced]
4276
- * @param {TimePosition} [position]
4245
+ * @param {TimelinePosition} [position]
4277
4246
  */
4278
4247
  sync(synced, position) {
4279
4248
  if (isUnd(synced) || synced && isUnd(synced.pause))
@@ -4285,7 +4254,7 @@ class Timeline extends Timer {
4285
4254
  /**
4286
4255
  * @param {TargetsParam} targets
4287
4256
  * @param {AnimationParams} parameters
4288
- * @param {TimePosition} [position]
4257
+ * @param {TimelinePosition} [position]
4289
4258
  * @return {this}
4290
4259
  */
4291
4260
  set(targets, parameters, position) {
@@ -4297,7 +4266,7 @@ class Timeline extends Timer {
4297
4266
  }
4298
4267
  /**
4299
4268
  * @param {Callback<Timer>} callback
4300
- * @param {TimePosition} [position]
4269
+ * @param {TimelinePosition} [position]
4301
4270
  * @return {this}
4302
4271
  */
4303
4272
  call(callback, position) {
@@ -4307,14 +4276,14 @@ class Timeline extends Timer {
4307
4276
  }
4308
4277
  /**
4309
4278
  * @param {String} labelName
4310
- * @param {TimePosition} [position]
4279
+ * @param {TimelinePosition} [position]
4311
4280
  * @return {this}
4312
4281
  *
4313
4282
  */
4314
4283
  label(labelName, position) {
4315
4284
  if (isUnd(labelName) || labelName && !isStr(labelName))
4316
4285
  return this;
4317
- this.labels[labelName] = parseTimelinePosition(this, /** @type TimePosition */ (position));
4286
+ this.labels[labelName] = parseTimelinePosition(this, position);
4318
4287
  return this;
4319
4288
  }
4320
4289
  /**
@@ -4382,11 +4351,39 @@ class Animatable {
4382
4351
  constructor(targets, parameters) {
4383
4352
  if (scope.current)
4384
4353
  scope.current.register(this);
4354
+ const beginHandler = () => {
4355
+ if (this.callbacks.completed)
4356
+ this.callbacks.reset();
4357
+ this.callbacks.play();
4358
+ };
4359
+ const pauseHandler = () => {
4360
+ if (this.callbacks.completed)
4361
+ return;
4362
+ let paused = true;
4363
+ for (let name in this.animations) {
4364
+ const anim = this.animations[name];
4365
+ if (!anim.paused && paused) {
4366
+ paused = false;
4367
+ break;
4368
+ }
4369
+ }
4370
+ if (paused) {
4371
+ this.callbacks.complete();
4372
+ }
4373
+ };
4374
+ /** @type {AnimationParams} */
4375
+ const globalParams = {
4376
+ onBegin: beginHandler,
4377
+ onComplete: pauseHandler,
4378
+ onPause: pauseHandler,
4379
+ };
4385
4380
  /** @type {AnimationParams} */
4386
- const globalParams = {};
4381
+ const callbacksAnimationParams = { v: 1, autoplay: false };
4387
4382
  const properties = {};
4388
4383
  this.targets = [];
4389
4384
  this.animations = {};
4385
+ /** @type {JSAnimation|null} */
4386
+ this.callbacks = null;
4390
4387
  if (isUnd(targets) || isUnd(parameters))
4391
4388
  return;
4392
4389
  for (let propName in parameters) {
@@ -4394,10 +4391,14 @@ class Animatable {
4394
4391
  if (isKey(propName)) {
4395
4392
  properties[propName] = paramValue;
4396
4393
  }
4394
+ else if (stringStartsWith(propName, 'on')) {
4395
+ callbacksAnimationParams[propName] = paramValue;
4396
+ }
4397
4397
  else {
4398
4398
  globalParams[propName] = paramValue;
4399
4399
  }
4400
4400
  }
4401
+ this.callbacks = new JSAnimation({ v: 0 }, callbacksAnimationParams);
4401
4402
  for (let propName in properties) {
4402
4403
  const propValue = properties[propName];
4403
4404
  const isObjValue = isObj(propValue);
@@ -4464,6 +4465,8 @@ class Animatable {
4464
4465
  }
4465
4466
  this.animations = {};
4466
4467
  this.targets.length = 0;
4468
+ if (this.callbacks)
4469
+ this.callbacks.revert();
4467
4470
  return this;
4468
4471
  }
4469
4472
  }
@@ -4479,6 +4482,7 @@ const createAnimatable = (targets, parameters) => /** @type {AnimatableObject} *
4479
4482
  * Spring ease solver adapted from https://webkit.org/demos/spring/spring.js
4480
4483
  * Webkit Copyright © 2016 Apple Inc
4481
4484
  */
4485
+ const maxSpringParamValue = K * 10;
4482
4486
  /**
4483
4487
  * @typedef {Object} SpringParams
4484
4488
  * @property {Number} [mass=1] - Mass, default 1
@@ -4497,10 +4501,10 @@ class Spring {
4497
4501
  this.maxDuration = 60000; // The maximum allowed spring duration in ms (default 1 min)
4498
4502
  this.maxRestSteps = this.restDuration / this.timeStep / K; // How many steps allowed after reaching restThreshold before stopping the duration calculation
4499
4503
  this.maxIterations = this.maxDuration / this.timeStep / K; // Calculate the maximum iterations allowed based on maxDuration
4500
- this.m = clamp(setValue(parameters.mass, 1), 0, K);
4501
- this.s = clamp(setValue(parameters.stiffness, 100), 1, K);
4502
- this.d = clamp(setValue(parameters.damping, 10), .1, K);
4503
- this.v = clamp(setValue(parameters.velocity, 0), -1e3, K);
4504
+ this.m = clamp(setValue(parameters.mass, 1), 0, maxSpringParamValue);
4505
+ this.s = clamp(setValue(parameters.stiffness, 100), 1, maxSpringParamValue);
4506
+ this.d = clamp(setValue(parameters.damping, 10), .1, maxSpringParamValue);
4507
+ this.v = clamp(setValue(parameters.velocity, 0), -1e4, maxSpringParamValue);
4504
4508
  this.w0 = 0;
4505
4509
  this.zeta = 0;
4506
4510
  this.wd = 0;
@@ -4549,28 +4553,28 @@ class Spring {
4549
4553
  return this.m;
4550
4554
  }
4551
4555
  set mass(v) {
4552
- this.m = clamp(setValue(v, 1), 0, K);
4556
+ this.m = clamp(setValue(v, 1), 0, maxSpringParamValue);
4553
4557
  this.compute();
4554
4558
  }
4555
4559
  get stiffness() {
4556
4560
  return this.s;
4557
4561
  }
4558
4562
  set stiffness(v) {
4559
- this.s = clamp(setValue(v, 100), 1, K);
4563
+ this.s = clamp(setValue(v, 100), 1, maxSpringParamValue);
4560
4564
  this.compute();
4561
4565
  }
4562
4566
  get damping() {
4563
4567
  return this.d;
4564
4568
  }
4565
4569
  set damping(v) {
4566
- this.d = clamp(setValue(v, 10), .1, K);
4570
+ this.d = clamp(setValue(v, 10), .1, maxSpringParamValue);
4567
4571
  this.compute();
4568
4572
  }
4569
4573
  get velocity() {
4570
4574
  return this.v;
4571
4575
  }
4572
4576
  set velocity(v) {
4573
- this.v = clamp(setValue(v, 0), -1e3, K);
4577
+ this.v = clamp(setValue(v, 0), -1e4, maxSpringParamValue);
4574
4578
  this.compute();
4575
4579
  }
4576
4580
  }
@@ -4871,36 +4875,27 @@ class Draggable {
4871
4875
  this.touchActionStyles = null;
4872
4876
  this.transforms = new Transforms(this.$target);
4873
4877
  this.overshootCoords = { x: 0, y: 0 };
4874
- this.overshootXTicker = new Timer({ autoplay: false }, null, 0).init();
4875
- this.overshootYTicker = new Timer({ autoplay: false }, null, 0).init();
4876
- this.updateTicker = new Timer({ autoplay: false }, null, 0).init();
4877
- this.overshootXTicker.onUpdate = () => {
4878
- if (this.disabled[0])
4879
- return;
4880
- this.updated = true;
4881
- this.manual = true;
4882
- this.animate[this.xProp](this.overshootCoords.x, 0);
4883
- };
4884
- this.overshootXTicker.onComplete = () => {
4885
- if (this.disabled[0])
4886
- return;
4887
- this.manual = false;
4888
- this.animate[this.xProp](this.overshootCoords.x, 0);
4889
- };
4890
- this.overshootYTicker.onUpdate = () => {
4891
- if (this.disabled[1])
4892
- return;
4893
- this.updated = true;
4894
- this.manual = true;
4895
- this.animate[this.yProp](this.overshootCoords.y, 0);
4896
- };
4897
- this.overshootYTicker.onComplete = () => {
4898
- if (this.disabled[1])
4899
- return;
4900
- this.manual = false;
4901
- this.animate[this.yProp](this.overshootCoords.y, 0);
4902
- };
4903
- this.updateTicker.onUpdate = () => this.update();
4878
+ this.overshootTicker = new Timer({
4879
+ autoplay: false,
4880
+ onUpdate: () => {
4881
+ this.updated = true;
4882
+ this.manual = true;
4883
+ // Use a duration of 1 to prevent the animatable from completing immediately to prevent issues with onSettle()
4884
+ // https://github.com/juliangarnier/anime/issues/1045
4885
+ if (!this.disabled[0])
4886
+ this.animate[this.xProp](this.overshootCoords.x, 1);
4887
+ if (!this.disabled[1])
4888
+ this.animate[this.yProp](this.overshootCoords.y, 1);
4889
+ },
4890
+ onComplete: () => {
4891
+ this.manual = false;
4892
+ if (!this.disabled[0])
4893
+ this.animate[this.xProp](this.overshootCoords.x, 0);
4894
+ if (!this.disabled[1])
4895
+ this.animate[this.yProp](this.overshootCoords.y, 0);
4896
+ },
4897
+ }, null, 0).init();
4898
+ this.updateTicker = new Timer({ autoplay: false, onUpdate: () => this.update() }, null, 0).init();
4904
4899
  this.contained = !isUnd(container);
4905
4900
  this.manual = false;
4906
4901
  this.grabbed = false;
@@ -4911,7 +4906,7 @@ class Draggable {
4911
4906
  this.enabled = false;
4912
4907
  this.initialized = false;
4913
4908
  this.activeProp = this.disabled[1] ? xProp : yProp;
4914
- this.animate.animations[this.activeProp].onRender = () => {
4909
+ this.animate.callbacks.onRender = () => {
4915
4910
  const hasUpdated = this.updated;
4916
4911
  const hasMoved = this.grabbed && hasUpdated;
4917
4912
  const hasReleased = !hasMoved && this.released;
@@ -4923,7 +4918,9 @@ class Draggable {
4923
4918
  this.deltaY = dy;
4924
4919
  this.coords[2] = x;
4925
4920
  this.coords[3] = y;
4926
- if (hasUpdated) {
4921
+ // Check if dx or dy are not 0 to check if the draggable has actually moved
4922
+ // https://github.com/juliangarnier/anime/issues/1032
4923
+ if (hasUpdated && (dx || dy)) {
4927
4924
  this.onUpdate(this);
4928
4925
  }
4929
4926
  if (!hasReleased) {
@@ -4934,9 +4931,9 @@ class Draggable {
4934
4931
  this.angle = atan2(dy, dx);
4935
4932
  }
4936
4933
  };
4937
- this.animate.animations[this.activeProp].onComplete = () => {
4934
+ this.animate.callbacks.onComplete = () => {
4938
4935
  if ((!this.grabbed && this.released)) {
4939
- // Set eleased to false before calling onSettle to avoid recursion
4936
+ // Set released to false before calling onSettle to avoid recursion
4940
4937
  this.released = false;
4941
4938
  }
4942
4939
  if (!this.manual) {
@@ -5006,7 +5003,7 @@ class Draggable {
5006
5003
  if (this.disabled[0])
5007
5004
  return;
5008
5005
  const v = round(x, 5);
5009
- this.overshootXTicker.pause();
5006
+ this.overshootTicker.pause();
5010
5007
  this.manual = true;
5011
5008
  this.updated = !muteUpdateCallback;
5012
5009
  this.destX = v;
@@ -5024,7 +5021,7 @@ class Draggable {
5024
5021
  if (this.disabled[1])
5025
5022
  return;
5026
5023
  const v = round(y, 5);
5027
- this.overshootYTicker.pause();
5024
+ this.overshootTicker.pause();
5028
5025
  this.manual = true;
5029
5026
  this.updated = !muteUpdateCallback;
5030
5027
  this.destY = v;
@@ -5071,6 +5068,10 @@ class Draggable {
5071
5068
  }
5072
5069
  updateBoundingValues() {
5073
5070
  const $container = this.$container;
5071
+ // Return early if no $container defined to prevents error when reading scrollWidth / scrollHeight
5072
+ // https://github.com/juliangarnier/anime/issues/1064
5073
+ if (!$container)
5074
+ return;
5074
5075
  const cx = this.x;
5075
5076
  const cy = this.y;
5076
5077
  const cx2 = this.coords[2];
@@ -5140,14 +5141,13 @@ class Draggable {
5140
5141
  this.setY(cy, true);
5141
5142
  }
5142
5143
  /**
5143
- * Returns 0 if not OB, 1 if x is OB, 2 if y is OB, 3 if both x and y are OB
5144
- *
5145
5144
  * @param {Array} bounds
5146
5145
  * @param {Number} x
5147
5146
  * @param {Number} y
5148
5147
  * @return {Number}
5149
5148
  */
5150
5149
  isOutOfBounds(bounds, x, y) {
5150
+ // Returns 0 if not OB, 1 if x is OB, 2 if y is OB, 3 if both x and y are OB
5151
5151
  if (!this.contained)
5152
5152
  return 0;
5153
5153
  const [bt, br, bb, bl] = bounds;
@@ -5278,8 +5278,7 @@ class Draggable {
5278
5278
  }
5279
5279
  stop() {
5280
5280
  this.updateTicker.pause();
5281
- this.overshootXTicker.pause();
5282
- this.overshootYTicker.pause();
5281
+ this.overshootTicker.pause();
5283
5282
  // Pauses the in bounds onRelease animations
5284
5283
  for (let prop in this.animate.animations)
5285
5284
  this.animate.animations[prop].pause();
@@ -5366,7 +5365,7 @@ class Draggable {
5366
5365
  */
5367
5366
  handleDown(e) {
5368
5367
  const $eTarget = /** @type {HTMLElement} */ (e.target);
5369
- if (this.grabbed || /** @type {HTMLInputElement} */ ($eTarget).type === 'range')
5368
+ if (this.grabbed || /** @type {HTMLInputElement} */ ($eTarget).type === 'range')
5370
5369
  return;
5371
5370
  e.stopPropagation();
5372
5371
  this.grabbed = true;
@@ -5565,8 +5564,7 @@ class Draggable {
5565
5564
  ease: releaseEase,
5566
5565
  composition,
5567
5566
  }).init();
5568
- this.overshootXTicker.stretch(durationX).restart();
5569
- this.overshootYTicker.stretch(durationY).restart();
5567
+ this.overshootTicker.stretch(max(durationX, durationY)).restart();
5570
5568
  }
5571
5569
  else {
5572
5570
  if (!disabledX)
@@ -5669,7 +5667,6 @@ class Draggable {
5669
5667
  this.targetStyles.revert();
5670
5668
  this.targetStyles = null;
5671
5669
  }
5672
- this.stop();
5673
5670
  this.$target.classList.add('is-disabled');
5674
5671
  this.$trigger.removeEventListener('touchstart', this);
5675
5672
  this.$trigger.removeEventListener('mousedown', this);
@@ -5687,10 +5684,10 @@ class Draggable {
5687
5684
  this.disable();
5688
5685
  this.$target.classList.remove('is-disabled');
5689
5686
  this.updateTicker.revert();
5690
- this.overshootXTicker.revert();
5691
- this.overshootYTicker.revert();
5687
+ this.overshootTicker.revert();
5692
5688
  this.resizeTicker.revert();
5693
5689
  this.animate.revert();
5690
+ this.resizeObserver.disconnect();
5694
5691
  return this;
5695
5692
  }
5696
5693
  /**
@@ -5959,14 +5956,11 @@ class Scope {
5959
5956
  */
5960
5957
  const createScope = params => new Scope(params);
5961
5958
 
5962
- /**
5963
- * @typedef {String|Number} ScrollThresholdValue
5964
- */
5965
5959
  /**
5966
5960
  * @return {Number}
5967
5961
  */
5968
5962
  const getMaxViewHeight = () => {
5969
- const $el = document.createElement('div');
5963
+ const $el = doc.createElement('div');
5970
5964
  doc.body.appendChild($el);
5971
5965
  $el.style.height = '100lvh';
5972
5966
  const height = $el.offsetHeight;
@@ -6110,8 +6104,8 @@ class ScrollContainer {
6110
6104
  }
6111
6105
  else {
6112
6106
  const elRect = $el.getBoundingClientRect();
6113
- width = elRect.width;
6114
- height = elRect.height;
6107
+ width = $el.clientWidth;
6108
+ height = $el.clientHeight;
6115
6109
  this.top = elRect.top;
6116
6110
  this.left = elRect.left;
6117
6111
  }
@@ -6157,7 +6151,7 @@ class ScrollContainer {
6157
6151
  this.dataTimer.cancel();
6158
6152
  this.resizeTicker.cancel();
6159
6153
  this.wakeTicker.cancel();
6160
- this.resizeObserver.unobserve(this.element);
6154
+ this.resizeObserver.disconnect();
6161
6155
  (this.useWin ? win : this.element).removeEventListener('scroll', this);
6162
6156
  scrollContainers.delete(this.element);
6163
6157
  }
@@ -6264,41 +6258,6 @@ const getAnimationDomTarget = linked => {
6264
6258
  };
6265
6259
  let scrollerIndex = 0;
6266
6260
  const debugColors$1 = ['#FF4B4B', '#FF971B', '#FFC730', '#F9F640', '#7AFF5A', '#18FF74', '#17E09B', '#3CFFEC', '#05DBE9', '#33B3F1', '#638CF9', '#C563FE', '#FF4FCF', '#F93F8A'];
6267
- /**
6268
- * @typedef {Object} ScrollThresholdParam
6269
- * @property {ScrollThresholdValue} [target]
6270
- * @property {ScrollThresholdValue} [container]
6271
- */
6272
- /**
6273
- * @callback ScrollObserverAxisCallback
6274
- * @param {ScrollObserver} self
6275
- * @return {'x'|'y'}
6276
- */
6277
- /**
6278
- * @callback ScrollThresholdCallback
6279
- * @param {ScrollObserver} self
6280
- * @return {ScrollThresholdValue|ScrollThresholdParam}
6281
- */
6282
- /**
6283
- * @typedef {Object} ScrollObserverParams
6284
- * @property {Number|String} [id]
6285
- * @property {Boolean|Number|String|EasingParam} [sync]
6286
- * @property {TargetsParam} [container]
6287
- * @property {TargetsParam} [target]
6288
- * @property {'x'|'y'|ScrollObserverAxisCallback|((observer: ScrollObserver) => 'x'|'y'|ScrollObserverAxisCallback)} [axis]
6289
- * @property {ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback|((observer: ScrollObserver) => ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback)} [enter]
6290
- * @property {ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback|((observer: ScrollObserver) => ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback)} [leave]
6291
- * @property {Boolean|((observer: ScrollObserver) => Boolean)} [repeat]
6292
- * @property {Boolean} [debug]
6293
- * @property {Callback<ScrollObserver>} [onEnter]
6294
- * @property {Callback<ScrollObserver>} [onLeave]
6295
- * @property {Callback<ScrollObserver>} [onEnterForward]
6296
- * @property {Callback<ScrollObserver>} [onLeaveForward]
6297
- * @property {Callback<ScrollObserver>} [onEnterBackward]
6298
- * @property {Callback<ScrollObserver>} [onLeaveBackward]
6299
- * @property {Callback<ScrollObserver>} [onUpdate]
6300
- * @property {Callback<ScrollObserver>} [onSyncComplete]
6301
- */
6302
6261
  class ScrollObserver {
6303
6262
  /**
6304
6263
  * @param {ScrollObserverParams} parameters
@@ -6381,8 +6340,8 @@ class ScrollObserver {
6381
6340
  this.forceEnter = false;
6382
6341
  /** @type {Boolean} */
6383
6342
  this.hasEntered = false;
6384
- /** @type {Array.<Number>} */
6385
- this.offsets = [];
6343
+ // /** @type {Array.<Number>} */
6344
+ // this.offsets = [];
6386
6345
  /** @type {Number} */
6387
6346
  this.offset = 0;
6388
6347
  /** @type {Number} */
@@ -6618,35 +6577,46 @@ class ScrollObserver {
6618
6577
  const linked = this.linked;
6619
6578
  let linkedTime;
6620
6579
  let $el = $target;
6621
- let offsetX = 0;
6622
- let offsetY = 0;
6580
+ // let offsetX = 0;
6581
+ // let offsetY = 0;
6582
+ // let $offsetParent = $el;
6623
6583
  /** @type {Element} */
6624
- let $offsetParent = $el;
6625
6584
  if (linked) {
6626
6585
  linkedTime = linked.currentTime;
6627
6586
  linked.seek(0, true);
6628
6587
  }
6629
- const isContainerStatic = getTargetValue(container.element, 'position') === 'static' ? setTargetValues(container.element, { position: 'relative ' }) : false;
6588
+ /* Old implementation to get offset and targetSize before fixing https://github.com/juliangarnier/anime/issues/1021
6589
+ // const isContainerStatic = getTargetValue(container.element, 'position') === 'static' ? setTargetValues(container.element, { position: 'relative '}) : false;
6590
+ // while ($el && $el !== container.element && $el !== doc.body) {
6591
+ // const isSticky = getTargetValue($el, 'position') === 'sticky' ?
6592
+ // setTargetValues($el, { position: 'static' }) :
6593
+ // false;
6594
+ // if ($el === $offsetParent) {
6595
+ // offsetX += $el.offsetLeft || 0;
6596
+ // offsetY += $el.offsetTop || 0;
6597
+ // $offsetParent = $el.offsetParent;
6598
+ // }
6599
+ // $el = /** @type {HTMLElement} */ ($el.parentElement);
6600
+ // if (isSticky) {
6601
+ // if (!stickys) stickys = [];
6602
+ // stickys.push(isSticky);
6603
+ // }
6604
+ // }
6605
+ // if (isContainerStatic) isContainerStatic.revert();
6606
+ // const offset = isHori ? offsetX : offsetY;
6607
+ // const targetSize = isHori ? $target.offsetWidth : $target.offsetHeight;
6630
6608
  while ($el && $el !== container.element && $el !== doc.body) {
6631
- const isSticky = getTargetValue($el, 'position') === 'sticky' ?
6632
- setTargetValues($el, { position: 'static' }) :
6633
- false;
6634
- if ($el === $offsetParent) {
6635
- offsetX += $el.offsetLeft || 0;
6636
- offsetY += $el.offsetTop || 0;
6637
- $offsetParent = $el.offsetParent;
6638
- }
6639
- $el = /** @type {HTMLElement} */ ($el.parentElement);
6609
+ const isSticky = getTargetValue($el, 'position') === 'sticky' ? setTargetValues($el, { position: 'static' }) : false;
6610
+ $el = $el.parentElement;
6640
6611
  if (isSticky) {
6641
6612
  if (!stickys)
6642
6613
  stickys = [];
6643
6614
  stickys.push(isSticky);
6644
6615
  }
6645
6616
  }
6646
- if (isContainerStatic)
6647
- isContainerStatic.revert();
6648
- const offset = isHori ? offsetX : offsetY;
6649
- const targetSize = isHori ? $target.offsetWidth : $target.offsetHeight;
6617
+ const rect = $target.getBoundingClientRect();
6618
+ const offset = isHori ? rect.left + container.scrollX - container.left : rect.top + container.scrollY - container.top;
6619
+ const targetSize = isHori ? rect.width : rect.height;
6650
6620
  const containerSize = isHori ? container.width : container.height;
6651
6621
  const scrollSize = isHori ? container.scrollWidth : container.scrollHeight;
6652
6622
  const maxScroll = scrollSize - containerSize;
@@ -6699,8 +6669,8 @@ class ScrollObserver {
6699
6669
  const offsetStart = parsedEnterTarget + offset - parsedEnterContainer;
6700
6670
  const offsetEnd = parsedLeaveTarget + offset - parsedLeaveContainer;
6701
6671
  const scrollDelta = offsetEnd - offsetStart;
6702
- this.offsets[0] = offsetX;
6703
- this.offsets[1] = offsetY;
6672
+ // this.offsets[0] = offsetX;
6673
+ // this.offsets[1] = offsetY;
6704
6674
  this.offset = offset;
6705
6675
  this.offsetStart = offsetStart;
6706
6676
  this.offsetEnd = offsetEnd;
@@ -6848,7 +6818,7 @@ class ScrollObserver {
6848
6818
  const onScroll = (parameters = {}) => new ScrollObserver(parameters);
6849
6819
 
6850
6820
 
6851
- const segmenter = !isUnd(Intl) && Intl.Segmenter;
6821
+ const segmenter = (typeof Intl !== 'undefined') && Intl.Segmenter;
6852
6822
  const valueRgx = /\{value\}/g;
6853
6823
  const indexRgx = /\{i\}/g;
6854
6824
  const whiteSpaceGroupRgx = /(\s+)/;
@@ -7315,9 +7285,33 @@ const text = {
7315
7285
 
7316
7286
 
7317
7287
  /**
7318
- * @param {Number|String|[Number|String,Number|String]} val
7319
- * @param {StaggerParams} params
7320
- * @return {StaggerFunction}
7288
+ * @overload
7289
+ * @param {Number} val
7290
+ * @param {StaggerParams} [params]
7291
+ * @return {StaggerFunction<Number>}
7292
+ */
7293
+ /**
7294
+ * @overload
7295
+ * @param {String} val
7296
+ * @param {StaggerParams} [params]
7297
+ * @return {StaggerFunction<String>}
7298
+ */
7299
+ /**
7300
+ * @overload
7301
+ * @param {[Number, Number]} val
7302
+ * @param {StaggerParams} [params]
7303
+ * @return {StaggerFunction<Number>}
7304
+ */
7305
+ /**
7306
+ * @overload
7307
+ * @param {[String, String]} val
7308
+ * @param {StaggerParams} [params]
7309
+ * @return {StaggerFunction<String>}
7310
+ */
7311
+ /**
7312
+ * @param {Number|String|[Number, Number]|[String, String]} val The staggered value or range
7313
+ * @param {StaggerParams} [params] The stagger parameters
7314
+ * @return {StaggerFunction<Number|String>}
7321
7315
  */
7322
7316
  const stagger = (val, params = {}) => {
7323
7317
  let values = [];