animejs
Version:
JavaScript animation engine
1,527 lines (1,323 loc) • 366 kB
JavaScript
/**
* Anime.js - ESM bundle
* @version v4.3.6
* @license MIT
* @copyright 2026 - Julian Garnier
*/
// Global types
/**
* @typedef {Object} DefaultsParams
* @property {Number|String} [id]
* @property {PercentageKeyframes|DurationKeyframes} [keyframes]
* @property {EasingParam} [playbackEase]
* @property {Number} [playbackRate]
* @property {Number} [frameRate]
* @property {Number|Boolean} [loop]
* @property {Boolean} [reversed]
* @property {Boolean} [alternate]
* @property {Boolean} [persist]
* @property {Boolean|ScrollObserver} [autoplay]
* @property {Number|FunctionValue} [duration]
* @property {Number|FunctionValue} [delay]
* @property {Number} [loopDelay]
* @property {EasingParam|FunctionValue} [ease]
* @property {'none'|'replace'|'blend'|compositionTypes} [composition]
* @property {(v: any) => any} [modifier]
* @property {Callback<Tickable>} [onBegin]
* @property {Callback<Tickable>} [onBeforeUpdate]
* @property {Callback<Tickable>} [onUpdate]
* @property {Callback<Tickable>} [onLoop]
* @property {Callback<Tickable>} [onPause]
* @property {Callback<Tickable>} [onComplete]
* @property {Callback<Renderable>} [onRender]
*/
/** @typedef {JSAnimation|Timeline} Renderable */
/** @typedef {Timer|Renderable} Tickable */
/** @typedef {Timer&JSAnimation&Timeline} CallbackArgument */
/** @typedef {Animatable|Tickable|WAAPIAnimation|Draggable|ScrollObserver|TextSplitter|Scope|AutoLayout} Revertible */
// Stagger types
/**
* @template T
* @callback StaggerFunction
* @param {Target} [target]
* @param {Number} [index]
* @param {Number} [length]
* @param {Timeline} [tl]
* @return {T}
*/
/**
* @typedef {Object} StaggerParams
* @property {Number|String} [start]
* @property {Number|'first'|'center'|'last'|'random'} [from]
* @property {Boolean} [reversed]
* @property {Array.<Number>} [grid]
* @property {('x'|'y')} [axis]
* @property {String|((target: Target, i: Number, length: Number) => Number)} [use]
* @property {Number} [total]
* @property {EasingParam} [ease]
* @property {TweenModifier} [modifier]
*/
// Targets types
/** @typedef {HTMLElement|SVGElement} DOMTarget */
/** @typedef {Record<String, any>} JSTarget */
/** @typedef {DOMTarget|JSTarget} Target */
/** @typedef {Target|NodeList|String} TargetSelector */
/** @typedef {DOMTarget|NodeList|String} DOMTargetSelector */
/** @typedef {Array.<DOMTargetSelector>|DOMTargetSelector} DOMTargetsParam */
/** @typedef {Array.<DOMTarget>} DOMTargetsArray */
/** @typedef {Array.<JSTarget>|JSTarget} JSTargetsParam */
/** @typedef {Array.<JSTarget>} JSTargetsArray */
/** @typedef {Array.<TargetSelector>|TargetSelector} TargetsParam */
/** @typedef {Array.<Target>} TargetsArray */
// Eases types
/**
* @callback EasingFunction
* @param {Number} time
* @return {Number}
*/
/**
* @typedef {('linear'|'none'|'in'|'out'|'inOut'|'inQuad'|'outQuad'|'inOutQuad'|'inCubic'|'outCubic'|'inOutCubic'|'inQuart'|'outQuart'|'inOutQuart'|'inQuint'|'outQuint'|'inOutQuint'|'inSine'|'outSine'|'inOutSine'|'inCirc'|'outCirc'|'inOutCirc'|'inExpo'|'outExpo'|'inOutExpo'|'inBounce'|'outBounce'|'inOutBounce'|'inBack'|'outBack'|'inOutBack'|'inElastic'|'outElastic'|'inOutElastic'|'out(p = 1.675)'|'inOut(p = 1.675)'|'inBack(overshoot = 1.7)'|'outBack(overshoot = 1.7)'|'inOutBack(overshoot = 1.7)'|'inElastic(amplitude = 1, period = .3)'|'outElastic(amplitude = 1, period = .3)'|'inOutElastic(amplitude = 1, period = .3)')} EaseStringParamNames
*/
/**
* @typedef {('ease'|'ease-in'|'ease-out'|'ease-in-out'|'linear(0, 0.25, 1)'|'steps'|'steps(6, start)'|'step-start'|'step-end'|'cubic-bezier(0.42, 0, 1, 1)') } WAAPIEaseStringParamNames
*/
/**
* @callback PowerEasing
* @param {Number|String} [power=1.675]
* @return {EasingFunction}
*/
/**
* @callback BackEasing
* @param {Number|String} [overshoot=1.7]
* @return {EasingFunction}
*/
/**
* @callback ElasticEasing
* @param {Number|String} [amplitude=1]
* @param {Number|String} [period=.3]
* @return {EasingFunction}
*/
/** @typedef {PowerEasing|BackEasing|ElasticEasing} EasingFunctionWithParams */
// A hack to get both ease names suggestions AND allow any strings
// https://github.com/microsoft/TypeScript/issues/29729#issuecomment-460346421
/** @typedef {(String & {})|EaseStringParamNames|EasingFunction|Spring} EasingParam */
/** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring} WAAPIEasingParam */
// Spring types
/**
* @typedef {Object} SpringParams
* @property {Number} [mass=1] - Mass, default 1
* @property {Number} [stiffness=100] - Stiffness, default 100
* @property {Number} [damping=10] - Damping, default 10
* @property {Number} [velocity=0] - Initial velocity, default 0
* @property {Number} [bounce=0] - Initial bounce, default 0
* @property {Number} [duration=0] - The perceived duration, default 0
* @property {Callback<JSAnimation>} [onComplete] - Callback function called when the spring currentTime hits the perceived duration
*/
// Callback types
/**
* @template T
* @callback Callback
* @param {T} self - Returns itself
* @param {PointerEvent} [e]
* @return {*}
*/
/**
* @template {object} T
* @typedef {Object} TickableCallbacks
* @property {Callback<T>} [onBegin]
* @property {Callback<T>} [onBeforeUpdate]
* @property {Callback<T>} [onUpdate]
* @property {Callback<T>} [onLoop]
* @property {Callback<T>} [onPause]
* @property {Callback<T>} [onComplete]
*/
/**
* @template {object} T
* @typedef {Object} RenderableCallbacks
* @property {Callback<T>} [onRender]
*/
// Timer types
/**
* @typedef {Object} TimerOptions
* @property {Number|String} [id]
* @property {TweenParamValue} [duration]
* @property {TweenParamValue} [delay]
* @property {Number} [loopDelay]
* @property {Boolean} [reversed]
* @property {Boolean} [alternate]
* @property {Boolean|Number} [loop]
* @property {Boolean|ScrollObserver} [autoplay]
* @property {Number} [frameRate]
* @property {Number} [playbackRate]
*/
/**
* @typedef {TimerOptions & TickableCallbacks<Timer>} TimerParams
*/
// Tween types
/**
* @callback FunctionValue
* @param {Target} target - The animated target
* @param {Number} index - The target index
* @param {Number} length - The total number of animated targets
* @return {Number|String|TweenObjectValue|EasingParam|Array.<Number|String|TweenObjectValue>}
*/
/**
* @callback TweenModifier
* @param {Number} value - The animated value
* @return {Number|String}
*/
/** @typedef {[Number, Number, Number, Number]} ColorArray */
/**
* @typedef {Object} Tween
* @property {Number} id
* @property {JSAnimation} parent
* @property {String} property
* @property {Target} target
* @property {String|Number} _value
* @property {Function|null} _toFunc
* @property {Function|null} _fromFunc
* @property {EasingFunction} _ease
* @property {Array.<Number>} _fromNumbers
* @property {Array.<Number>} _toNumbers
* @property {Array.<String>} _strings
* @property {Number} _fromNumber
* @property {Number} _toNumber
* @property {Array.<Number>} _numbers
* @property {Number} _number
* @property {String} _unit
* @property {TweenModifier} _modifier
* @property {Number} _currentTime
* @property {Number} _delay
* @property {Number} _updateDuration
* @property {Number} _startTime
* @property {Number} _changeDuration
* @property {Number} _absoluteStartTime
* @property {tweenTypes} _tweenType
* @property {valueTypes} _valueType
* @property {Number} _composition
* @property {Number} _isOverlapped
* @property {Number} _isOverridden
* @property {Number} _renderTransforms
* @property {String} _inlineValue
* @property {Tween} _prevRep
* @property {Tween} _nextRep
* @property {Tween} _prevAdd
* @property {Tween} _nextAdd
* @property {Tween} _prev
* @property {Tween} _next
*/
/**
* @typedef TweenDecomposedValue
* @property {Number} t - Type
* @property {Number} n - Single number value
* @property {String} u - Value unit
* @property {String} o - Value operator
* @property {Array.<Number>} d - Array of Numbers (in case of complex value type)
* @property {Array.<String>} s - Strings (in case of complex value type)
*/
/** @typedef {{_head: null|Tween, _tail: null|Tween}} TweenPropertySiblings */
/** @typedef {Record<String, TweenPropertySiblings>} TweenLookups */
/** @typedef {WeakMap.<Target, TweenLookups>} TweenReplaceLookups */
/** @typedef {Map.<Target, TweenLookups>} TweenAdditiveLookups */
// JSAnimation types
/**
* @typedef {Number|String|FunctionValue|EasingParam} TweenParamValue
*/
/**
* @typedef {TweenParamValue|[TweenParamValue, TweenParamValue]} TweenPropValue
*/
/**
* @typedef {(String & {})|'none'|'replace'|'blend'|compositionTypes} TweenComposition
*/
/**
* @typedef {Object} TweenParamsOptions
* @property {TweenParamValue} [duration]
* @property {TweenParamValue} [delay]
* @property {EasingParam|FunctionValue} [ease]
* @property {TweenModifier} [modifier]
* @property {TweenComposition} [composition]
*/
/**
* @typedef {Object} TweenValues
* @property {TweenParamValue} [from]
* @property {TweenPropValue} [to]
* @property {TweenPropValue} [fromTo]
*/
/**
* @typedef {TweenParamsOptions & TweenValues} TweenKeyValue
*/
/**
* @typedef {Array.<TweenKeyValue|TweenPropValue>} ArraySyntaxValue
*/
/**
* @typedef {TweenParamValue|ArraySyntaxValue|TweenKeyValue} TweenOptions
*/
/**
* @typedef {Partial<{to: TweenParamValue|Array.<TweenParamValue>; from: TweenParamValue|Array.<TweenParamValue>; fromTo: TweenParamValue|Array.<TweenParamValue>;}>} TweenObjectValue
*/
/**
* @typedef {Object} PercentageKeyframeOptions
* @property {EasingParam} [ease]
*/
/**
* @typedef {Record<String, TweenParamValue>} PercentageKeyframeParams
*/
/**
* @typedef {Record<String, PercentageKeyframeParams & PercentageKeyframeOptions>} PercentageKeyframes
*/
/**
* @typedef {Array<Record<String, TweenOptions | TweenModifier | boolean> & TweenParamsOptions>} DurationKeyframes
*/
/**
* @typedef {Object} AnimationOptions
* @property {PercentageKeyframes|DurationKeyframes} [keyframes]
* @property {EasingParam} [playbackEase]
*/
// TODO: Currently setting TweenModifier to the intersected Record<> makes the FunctionValue type target param any if only one parameter is set
/**
* @typedef {Record<String, TweenOptions | Callback<JSAnimation> | TweenModifier | boolean | PercentageKeyframes | DurationKeyframes | ScrollObserver> & TimerOptions & AnimationOptions & TweenParamsOptions & TickableCallbacks<JSAnimation> & RenderableCallbacks<JSAnimation>} AnimationParams
*/
// Timeline types
/**
* Accepts:<br>
* - `Number` - Absolute position in milliseconds (e.g., `500` places element at exactly 500ms)<br>
* - `'+=Number'` - Addition: Position element X ms after the last element (e.g., `'+=100'`)<br>
* - `'-=Number'` - Subtraction: Position element X ms before the last element's end (e.g., `'-=100'`)<br>
* - `'*=Number'` - Multiplier: Position element at a fraction of the total duration (e.g., `'*=.5'` for halfway)<br>
* - `'<'` - Previous end: Position element at the end position of the previous element<br>
* - `'<<'` - Previous start: Position element at the start position of the previous element<br>
* - `'<<+=Number'` - Combined: Position element relative to previous element's start (e.g., `'<<+=250'`)<br>
* - `'label'` - Label: Position element at a named label position (e.g., `'My Label'`)
*
* @typedef {Number|`+=${Number}`|`-=${Number}`|`*=${Number}`|'<'|'<<'|`<<+=${Number}`|`<<-=${Number}`|String} TimelinePosition
*/
/**
* Accepts:<br>
* - `Number` - Absolute position in milliseconds (e.g., `500` places animation at exactly 500ms)<br>
* - `'+=Number'` - Addition: Position animation X ms after the last animation (e.g., `'+=100'`)<br>
* - `'-=Number'` - Subtraction: Position animation X ms before the last animation's end (e.g., `'-=100'`)<br>
* - `'*=Number'` - Multiplier: Position animation at a fraction of the total duration (e.g., `'*=.5'` for halfway)<br>
* - `'<'` - Previous end: Position animation at the end position of the previous animation<br>
* - `'<<'` - Previous start: Position animation at the start position of the previous animation<br>
* - `'<<+=Number'` - Combined: Position animation relative to previous animation's start (e.g., `'<<+=250'`)<br>
* - `'label'` - Label: Position animation at a named label position (e.g., `'My Label'`)<br>
* - `stagger(String|Nummber)` - Stagger multi-elements animation positions (e.g., 10, 20, 30...)
*
* @typedef {TimelinePosition | StaggerFunction<Number|String>} TimelineAnimationPosition
*/
/**
* @typedef {Object} TimelineOptions
* @property {DefaultsParams} [defaults]
* @property {EasingParam} [playbackEase]
* @property {Boolean} [composition]
*/
/**
* @typedef {TimerOptions & TimelineOptions & TickableCallbacks<Timeline> & RenderableCallbacks<Timeline>} TimelineParams
*/
// WAAPIAnimation types
/**
* @typedef {String|Number|Array<String>|Array<Number>} WAAPITweenValue
*/
/**
* @callback WAAPIFunctionValue
* @param {DOMTarget} target - The animated target
* @param {Number} index - The target index
* @param {Number} length - The total number of animated targets
* @return {WAAPITweenValue|WAAPIEasingParam}
*/
/**
* @typedef {WAAPITweenValue|WAAPIFunctionValue|Array<String|Number|WAAPIFunctionValue>} WAAPIKeyframeValue
*/
/**
* @typedef {Object} WAAPITweenOptions
* @property {WAAPIKeyframeValue} [to]
* @property {WAAPIKeyframeValue} [from]
* @property {Number|WAAPIFunctionValue} [duration]
* @property {Number|WAAPIFunctionValue} [delay]
* @property {WAAPIEasingParam} [ease]
* @property {CompositeOperation} [composition]
*/
/**
* @typedef {Object} WAAPIAnimationOptions
* @property {Number|Boolean} [loop]
* @property {Boolean} [Reversed]
* @property {Boolean} [Alternate]
* @property {Boolean|ScrollObserver} [autoplay]
* @property {Number} [playbackRate]
* @property {Number|WAAPIFunctionValue} [duration]
* @property {Number|WAAPIFunctionValue} [delay]
* @property {WAAPIEasingParam|WAAPIFunctionValue} [ease]
* @property {CompositeOperation} [composition]
* @property {Boolean} [persist]
* @property {Callback<WAAPIAnimation>} [onComplete]
*/
/**
* @typedef {Record<String, WAAPIKeyframeValue | WAAPIAnimationOptions | Boolean | ScrollObserver | Callback<WAAPIAnimation> | WAAPIEasingParam | WAAPITweenOptions> & WAAPIAnimationOptions} WAAPIAnimationParams
*/
// Animatable types
/**
* @callback AnimatablePropertySetter
* @param {Number|Array.<Number>} to
* @param {Number} [duration]
* @param {EasingParam} [ease]
* @return {AnimatableObject}
*/
/**
* @callback AnimatablePropertyGetter
* @return {Number|Array.<Number>}
*/
/**
* @typedef {AnimatablePropertySetter & AnimatablePropertyGetter} AnimatableProperty
*/
/**
* @typedef {Animatable & Record<String, AnimatableProperty>} AnimatableObject
*/
/**
* @typedef {Object} AnimatablePropertyParamsOptions
* @property {String} [unit]
* @property {TweenParamValue} [duration]
* @property {EasingParam} [ease]
* @property {TweenModifier} [modifier]
* @property {TweenComposition} [composition]
*/
/**
* @typedef {Record<String, TweenParamValue | EasingParam | TweenModifier | TweenComposition | AnimatablePropertyParamsOptions> & AnimatablePropertyParamsOptions} AnimatableParams
*/
// Scope types
/**
* @typedef {Object} ReactRef
* @property {HTMLElement|SVGElement|null} [current]
*/
/**
* @typedef {Object} AngularRef
* @property {HTMLElement|SVGElement} [nativeElement]
*/
/**
* @typedef {Object} ScopeParams
* @property {DOMTargetSelector|ReactRef|AngularRef} [root]
* @property {DefaultsParams} [defaults]
* @property {Record<String, String>} [mediaQueries]
*/
/**
* @template T
* @callback ScopedCallback
* @param {Scope} scope
* @return {T}
*/
/**
* @callback ScopeCleanupCallback
* @param {Scope} [scope]
*/
/**
* @callback ScopeConstructorCallback
* @param {Scope} [scope]
* @return {ScopeCleanupCallback|void}
*/
/**
* @callback ScopeMethod
* @param {...*} args
* @return {ScopeCleanupCallback|void}
*/
// Scroll types
/**
* @typedef {String|Number} ScrollThresholdValue
*/
/**
* @typedef {Object} ScrollThresholdParam
* @property {ScrollThresholdValue} [target]
* @property {ScrollThresholdValue} [container]
*/
/**
* @callback ScrollObserverAxisCallback
* @param {ScrollObserver} self
* @return {'x'|'y'}
*/
/**
* @callback ScrollThresholdCallback
* @param {ScrollObserver} self
* @return {ScrollThresholdValue|ScrollThresholdParam}
*/
/**
* @typedef {Object} ScrollObserverParams
* @property {Number|String} [id]
* @property {Boolean|Number|String|EasingParam} [sync]
* @property {TargetsParam} [container]
* @property {TargetsParam} [target]
* @property {'x'|'y'|ScrollObserverAxisCallback|((observer: ScrollObserver) => 'x'|'y'|ScrollObserverAxisCallback)} [axis]
* @property {ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback|((observer: ScrollObserver) => ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback)} [enter]
* @property {ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback|((observer: ScrollObserver) => ScrollThresholdValue|ScrollThresholdParam|ScrollThresholdCallback)} [leave]
* @property {Boolean|((observer: ScrollObserver) => Boolean)} [repeat]
* @property {Boolean} [debug]
* @property {Callback<ScrollObserver>} [onEnter]
* @property {Callback<ScrollObserver>} [onLeave]
* @property {Callback<ScrollObserver>} [onEnterForward]
* @property {Callback<ScrollObserver>} [onLeaveForward]
* @property {Callback<ScrollObserver>} [onEnterBackward]
* @property {Callback<ScrollObserver>} [onLeaveBackward]
* @property {Callback<ScrollObserver>} [onUpdate]
* @property {Callback<ScrollObserver>} [onResize]
* @property {Callback<ScrollObserver>} [onSyncComplete]
*/
// Draggable types
/**
* @typedef {Object} DraggableAxisParam
* @property {String} [mapTo]
* @property {TweenModifier} [modifier]
* @property {TweenComposition} [composition]
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
*/
/**
* @typedef {Object} DraggableCursorParams
* @property {String} [onHover]
* @property {String} [onGrab]
*/
/**
* @typedef {Object} DraggableDragThresholdParams
* @property {Number} [mouse]
* @property {Number} [touch]
*/
/**
* @typedef {Object} DraggableParams
* @property {DOMTargetSelector} [trigger]
* @property {DOMTargetSelector|Array<Number>|((draggable: Draggable) => DOMTargetSelector|Array<Number>)} [container]
* @property {Boolean|DraggableAxisParam} [x]
* @property {Boolean|DraggableAxisParam} [y]
* @property {TweenModifier} [modifier]
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [containerPadding]
* @property {Number|((draggable: Draggable) => Number)} [containerFriction]
* @property {Number|((draggable: Draggable) => Number)} [releaseContainerFriction]
* @property {Number|((draggable: Draggable) => Number)} [dragSpeed]
* @property {Number|DraggableDragThresholdParams|((draggable: Draggable) => Number|DraggableDragThresholdParams)} [dragThreshold]
* @property {Number|((draggable: Draggable) => Number)} [scrollSpeed]
* @property {Number|((draggable: Draggable) => Number)} [scrollThreshold]
* @property {Number|((draggable: Draggable) => Number)} [minVelocity]
* @property {Number|((draggable: Draggable) => Number)} [maxVelocity]
* @property {Number|((draggable: Draggable) => Number)} [velocityMultiplier]
* @property {Number} [releaseMass]
* @property {Number} [releaseStiffness]
* @property {Number} [releaseDamping]
* @property {Boolean} [releaseDamping]
* @property {EasingParam} [releaseEase]
* @property {Boolean|DraggableCursorParams|((draggable: Draggable) => Boolean|DraggableCursorParams)} [cursor]
* @property {Callback<Draggable>} [onGrab]
* @property {Callback<Draggable>} [onDrag]
* @property {Callback<Draggable>} [onRelease]
* @property {Callback<Draggable>} [onUpdate]
* @property {Callback<Draggable>} [onSettle]
* @property {Callback<Draggable>} [onSnap]
* @property {Callback<Draggable>} [onResize]
* @property {Callback<Draggable>} [onAfterResize]
*/
// Text types
/**
* @typedef {Object} SplitTemplateParams
* @property {false|String} [class]
* @property {Boolean|'hidden'|'clip'|'visible'|'scroll'|'auto'} [wrap]
* @property {Boolean|'top'|'right'|'bottom'|'left'|'center'} [clone]
*/
/**
* @typedef {Boolean|String} SplitValue
*/
/**
* @callback SplitFunctionValue
* @param {Node|HTMLElement} [value]
* @return String
*/
/**
* @typedef {Object} TextSplitterParams
* @property {SplitValue|SplitTemplateParams|SplitFunctionValue} [lines]
* @property {SplitValue|SplitTemplateParams|SplitFunctionValue} [words]
* @property {SplitValue|SplitTemplateParams|SplitFunctionValue} [chars]
* @property {Boolean} [accessible]
* @property {Boolean} [includeSpaces]
* @property {Boolean} [debug]
*/
// SVG types
/**
* @typedef {SVGGeometryElement & {
* setAttribute(name: 'draw', value: `${number} ${number}`): void;
* draw: `${number} ${number}`;
* }} DrawableSVGGeometry
*/
// Environments
// TODO: Do we need to check if we're running inside a worker ?
const isBrowser = typeof window !== 'undefined';
/** @typedef {Window & {AnimeJS: Array} & {AnimeJSDevTools: any}|null} AnimeJSWindow
/** @type {AnimeJSWindow} */
const win = isBrowser ? /** @type {AnimeJSWindow} */(/** @type {unknown} */(window)) : null;
/** @type {Document|null} */
const doc = isBrowser ? document : null;
// Enums
/** @enum {Number} */
const tweenTypes = {
OBJECT: 0,
ATTRIBUTE: 1,
CSS: 2,
TRANSFORM: 3,
CSS_VAR: 4,
};
/** @enum {Number} */
const valueTypes = {
NUMBER: 0,
UNIT: 1,
COLOR: 2,
COMPLEX: 3,
};
/** @enum {Number} */
const tickModes = {
NONE: 0,
AUTO: 1,
FORCE: 2,
};
/** @enum {Number} */
const compositionTypes = {
replace: 0,
none: 1,
blend: 2,
};
// Cache symbols
const isRegisteredTargetSymbol = Symbol();
const isDomSymbol = Symbol();
const isSvgSymbol = Symbol();
const transformsSymbol = Symbol();
const morphPointsSymbol = Symbol();
const proxyTargetSymbol = Symbol();
// Numbers
const minValue = 1e-11;
const maxValue = 1e12;
const K = 1e3;
const maxFps = 240;
// Strings
const emptyString = '';
const cssVarPrefix = 'var(';
const shortTransforms = /*#__PURE__*/ (() => {
const map = new Map();
map.set('x', 'translateX');
map.set('y', 'translateY');
map.set('z', 'translateZ');
return map;
})();
const validTransforms = [
'translateX',
'translateY',
'translateZ',
'rotate',
'rotateX',
'rotateY',
'rotateZ',
'scale',
'scaleX',
'scaleY',
'scaleZ',
'skew',
'skewX',
'skewY',
'matrix',
'matrix3d',
'perspective',
];
const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({...a, [v]: v + '('}), {});
// Functions
/** @return {void} */
const noop = () => {};
// Regex
const validRgbHslRgx = /\)\s*[-.\d]/;
const hexTestRgx = /(^#([\da-f]{3}){1,2}$)|(^#([\da-f]{4}){1,2}$)/i;
const rgbExecRgx = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i;
const rgbaExecRgx = /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/i;
const hslExecRgx = /hsl\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*\)/i;
const hslaExecRgx = /hsla\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/i;
// export const digitWithExponentRgx = /[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?/g;
const digitWithExponentRgx = /[-+]?\d*\.?\d+(?:e[-+]?\d)?/gi;
// export const unitsExecRgx = /^([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)+([a-z]+|%)$/i;
const unitsExecRgx = /^([-+]?\d*\.?\d+(?:e[-+]?\d+)?)([a-z]+|%)$/i;
const lowerCaseRgx = /([a-z])([A-Z])/g;
const transformsExecRgx = /(\w+)(\([^)]+\)+)/g; // Match inline transforms with cacl() values, returns the value wrapped in ()
const relativeValuesExecRgx = /(\*=|\+=|-=)/;
const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/;
/** @type {DefaultsParams} */
const defaults = {
id: null,
keyframes: null,
playbackEase: null,
playbackRate: 1,
frameRate: maxFps,
loop: 0,
reversed: false,
alternate: false,
autoplay: true,
persist: false,
duration: K,
delay: 0,
loopDelay: 0,
ease: 'out(2)',
composition: compositionTypes.replace,
modifier: v => v,
onBegin: noop,
onBeforeUpdate: noop,
onUpdate: noop,
onLoop: noop,
onPause: noop,
onComplete: noop,
onRender: noop,
};
const scope = {
/** @type {Scope} */
current: null,
/** @type {Document|DOMTarget} */
root: doc,
};
const globals = {
/** @type {DefaultsParams} */
defaults,
/** @type {Number} */
precision: 4,
/** @type {Number} equals 1 in ms mode, 0.001 in s mode */
timeScale: 1,
/** @type {Number} */
tickThreshold: 200,
};
const devTools = isBrowser && win.AnimeJSDevTools;
const globalVersions = { version: '4.3.6', engine: null };
if (isBrowser) {
if (!win.AnimeJS) win.AnimeJS = [];
win.AnimeJS.push(globalVersions);
}
// Strings
/**
* @param {String} str
* @return {String}
*/
const toLowerCase = str => str.replace(lowerCaseRgx, '$1-$2').toLowerCase();
/**
* Prioritize this method instead of regex when possible
* @param {String} str
* @param {String} sub
* @return {Boolean}
*/
const stringStartsWith = (str, sub) => str.indexOf(sub) === 0;
// 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.
const now = Date.now;
// Types checkers
const isArr = Array.isArray;
/**@param {any} a @return {a is Record<String, any>} */
const isObj = a => a && a.constructor === Object;
/**@param {any} a @return {a is Number} */
const isNum = a => typeof a === 'number' && !isNaN(a);
/**@param {any} a @return {a is String} */
const isStr = a => typeof a === 'string';
/**@param {any} a @return {a is Function} */
const isFnc = a => typeof a === 'function';
/**@param {any} a @return {a is undefined} */
const isUnd = a => typeof a === 'undefined';
/**@param {any} a @return {a is null | undefined} */
const isNil = a => isUnd(a) || a === null;
/**@param {any} a @return {a is SVGElement} */
const isSvg = a => isBrowser && a instanceof SVGElement;
/**@param {any} a @return {Boolean} */
const isHex = a => hexTestRgx.test(a);
/**@param {any} a @return {Boolean} */
const isRgb = a => stringStartsWith(a, 'rgb');
/**@param {any} a @return {Boolean} */
const isHsl = a => stringStartsWith(a, 'hsl');
/**@param {any} a @return {Boolean} */ // Make sure boxShadow syntax like 'rgb(255, 0, 0) 0px 0px 6px 0px' is not a valid color type
const isCol = a => isHex(a) || ((isRgb(a) || isHsl(a)) && (a[a.length - 1] === ')' || !validRgbHslRgx.test(a)));
/**@param {any} a @return {Boolean} */
const isKey = a => !globals.defaults.hasOwnProperty(a);
// SVG
// Consider the following as CSS animation
// CSS opacity animation has better default values (opacity: 1 instead of 0))
// rotate is more commonly intended to be used as a transform
const svgCssReservedProperties = ['opacity', 'rotate', 'overflow', 'color'];
/**
* @param {Target} el
* @param {String} propertyName
* @return {Boolean}
*/
const isValidSVGAttribute = (el, propertyName) => {
if (svgCssReservedProperties.includes(propertyName)) return false;
if (el.getAttribute(propertyName) || propertyName in el) {
if (propertyName === 'scale') { // Scale
const elParentNode = /** @type {SVGGeometryElement} */(/** @type {DOMTarget} */(el).parentNode);
// Only consider scale as a valid SVG attribute on filter element
return elParentNode && elParentNode.tagName === 'filter';
}
return true;
}
};
// Number
/**
* @param {Number|String} str
* @return {Number}
*/
const parseNumber = str => isStr(str) ?
parseFloat(/** @type {String} */(str)) :
/** @type {Number} */(str);
// Math
const pow = Math.pow;
const sqrt = Math.sqrt;
const sin = Math.sin;
const cos = Math.cos;
const abs = Math.abs;
const exp = Math.exp;
const ceil = Math.ceil;
const floor = Math.floor;
const asin = Math.asin;
const max = Math.max;
const atan2 = Math.atan2;
const PI = Math.PI;
const _round = Math.round;
/**
* Clamps a value between min and max bounds
*
* @param {Number} v - Value to clamp
* @param {Number} min - Minimum boundary
* @param {Number} max - Maximum boundary
* @return {Number}
*/
const clamp$1 = (v, min, max) => v < min ? min : v > max ? max : v;
const powCache = {};
/**
* Rounds a number to specified decimal places
*
* @param {Number} v - Value to round
* @param {Number} decimalLength - Number of decimal places
* @return {Number}
*/
const round$1 = (v, decimalLength) => {
if (decimalLength < 0) return v;
if (!decimalLength) return _round(v);
let p = powCache[decimalLength];
if (!p) p = powCache[decimalLength] = 10 ** decimalLength;
return _round(v * p) / p;
};
/**
* Snaps a value to nearest increment or array value
*
* @param {Number} v - Value to snap
* @param {Number|Array<Number>} increment - Step size or array of snap points
* @return {Number}
*/
const snap$1 = (v, increment) => isArr(increment) ? increment.reduce((closest, cv) => (abs(cv - v) < abs(closest - v) ? cv : closest)) : increment ? _round(v / increment) * increment : v;
/**
* Linear interpolation between two values
*
* @param {Number} start - Starting value
* @param {Number} end - Ending value
* @param {Number} factor - Interpolation factor in the range [0, 1]
* @return {Number} The interpolated value
*/
const lerp$1 = (start, end, factor) => start + (end - start) * factor;
/**
* Replaces infinity with maximum safe value
*
* @param {Number} v - Value to check
* @return {Number}
*/
const clampInfinity = v => v === Infinity ? maxValue : v === -Infinity ? -maxValue : v;
/**
* Normalizes time value with minimum threshold
*
* @param {Number} v - Time value to normalize
* @return {Number}
*/
const normalizeTime = v => v <= minValue ? minValue : clampInfinity(round$1(v, 11));
// Arrays
/**
* @template T
* @param {T[]} a
* @return {T[]}
*/
const cloneArray = a => isArr(a) ? [ ...a ] : a;
// Objects
/**
* @template T
* @template U
* @param {T} o1
* @param {U} o2
* @return {T & U}
*/
const mergeObjects = (o1, o2) => {
const merged = /** @type {T & U} */({ ...o1 });
for (let p in o2) {
const o1p = /** @type {T & U} */(o1)[p];
merged[p] = isUnd(o1p) ? /** @type {T & U} */(o2)[p] : o1p;
} return merged;
};
// Linked lists
/**
* @param {Object} parent
* @param {Function} callback
* @param {Boolean} [reverse]
* @param {String} [prevProp]
* @param {String} [nextProp]
* @return {void}
*/
const forEachChildren = (parent, callback, reverse, prevProp = '_prev', nextProp = '_next') => {
let next = parent._head;
let adjustedNextProp = nextProp;
if (reverse) {
next = parent._tail;
adjustedNextProp = prevProp;
}
while (next) {
const currentNext = next[adjustedNextProp];
callback(next);
next = currentNext;
}
};
/**
* @param {Object} parent
* @param {Object} child
* @param {String} [prevProp]
* @param {String} [nextProp]
* @return {void}
*/
const removeChild = (parent, child, prevProp = '_prev', nextProp = '_next') => {
const prev = child[prevProp];
const next = child[nextProp];
prev ? prev[nextProp] = next : parent._head = next;
next ? next[prevProp] = prev : parent._tail = prev;
child[prevProp] = null;
child[nextProp] = null;
};
/**
* @param {Object} parent
* @param {Object} child
* @param {Function} [sortMethod]
* @param {String} prevProp
* @param {String} nextProp
* @return {void}
*/
const addChild = (parent, child, sortMethod, prevProp = '_prev', nextProp = '_next') => {
let prev = parent._tail;
while (prev && sortMethod && sortMethod(prev, child)) prev = prev[prevProp];
const next = prev ? prev[nextProp] : parent._head;
prev ? prev[nextProp] = child : parent._head = child;
next ? next[prevProp] = child : parent._tail = child;
child[prevProp] = prev;
child[nextProp] = next;
};
/**
* @param {DOMTarget} target
* @param {String} propName
* @param {Object} animationInlineStyles
* @return {String}
*/
const parseInlineTransforms = (target, propName, animationInlineStyles) => {
const inlineTransforms = target.style.transform;
let inlinedStylesPropertyValue;
if (inlineTransforms) {
const cachedTransforms = target[transformsSymbol];
let t; while (t = transformsExecRgx.exec(inlineTransforms)) {
const inlinePropertyName = t[1];
// const inlinePropertyValue = t[2];
const inlinePropertyValue = t[2].slice(1, -1);
cachedTransforms[inlinePropertyName] = inlinePropertyValue;
if (inlinePropertyName === propName) {
inlinedStylesPropertyValue = inlinePropertyValue;
// Store the new parsed inline styles if animationInlineStyles is provided
if (animationInlineStyles) {
animationInlineStyles[propName] = inlinePropertyValue;
}
}
}
}
return inlineTransforms && !isUnd(inlinedStylesPropertyValue) ? inlinedStylesPropertyValue :
stringStartsWith(propName, 'scale') ? '1' :
stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew') ? '0deg' : '0px';
};
/**
* RGB / RGBA Color value string -> RGBA values array
* @param {String} rgbValue
* @return {ColorArray}
*/
const rgbToRgba = rgbValue => {
const rgba = rgbExecRgx.exec(rgbValue) || rgbaExecRgx.exec(rgbValue);
const a = !isUnd(rgba[4]) ? +rgba[4] : 1;
return [
+rgba[1],
+rgba[2],
+rgba[3],
a
]
};
/**
* HEX3 / HEX3A / HEX6 / HEX6A Color value string -> RGBA values array
* @param {String} hexValue
* @return {ColorArray}
*/
const hexToRgba = hexValue => {
const hexLength = hexValue.length;
const isShort = hexLength === 4 || hexLength === 5;
return [
+('0x' + hexValue[1] + hexValue[isShort ? 1 : 2]),
+('0x' + hexValue[isShort ? 2 : 3] + hexValue[isShort ? 2 : 4]),
+('0x' + hexValue[isShort ? 3 : 5] + hexValue[isShort ? 3 : 6]),
((hexLength === 5 || hexLength === 9) ? +(+('0x' + hexValue[isShort ? 4 : 7] + hexValue[isShort ? 4 : 8]) / 255).toFixed(3) : 1)
]
};
/**
* @param {Number} p
* @param {Number} q
* @param {Number} t
* @return {Number}
*/
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
return t < 1 / 6 ? p + (q - p) * 6 * t :
t < 1 / 2 ? q :
t < 2 / 3 ? p + (q - p) * (2 / 3 - t) * 6 :
p;
};
/**
* HSL / HSLA Color value string -> RGBA values array
* @param {String} hslValue
* @return {ColorArray}
*/
const hslToRgba = hslValue => {
const hsla = hslExecRgx.exec(hslValue) || hslaExecRgx.exec(hslValue);
const h = +hsla[1] / 360;
const s = +hsla[2] / 100;
const l = +hsla[3] / 100;
const a = !isUnd(hsla[4]) ? +hsla[4] : 1;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const q = l < .5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = round$1(hue2rgb(p, q, h + 1 / 3) * 255, 0);
g = round$1(hue2rgb(p, q, h) * 255, 0);
b = round$1(hue2rgb(p, q, h - 1 / 3) * 255, 0);
}
return [r, g, b, a];
};
/**
* All in one color converter that converts a color string value into an array of RGBA values
* @param {String} colorString
* @return {ColorArray}
*/
const convertColorStringValuesToRgbaArray = colorString => {
return isRgb(colorString) ? rgbToRgba(colorString) :
isHex(colorString) ? hexToRgba(colorString) :
isHsl(colorString) ? hslToRgba(colorString) :
[0, 0, 0, 1];
};
/**
* @template T, D
* @param {T|undefined} targetValue
* @param {D} defaultValue
* @return {T|D}
*/
const setValue = (targetValue, defaultValue) => {
return isUnd(targetValue) ? defaultValue : targetValue;
};
/**
* @param {TweenPropValue} value
* @param {Target} target
* @param {Number} index
* @param {Number} total
* @param {Object} [store]
* @return {any}
*/
const getFunctionValue = (value, target, index, total, store) => {
let func;
if (isFnc(value)) {
func = () => {
const computed = /** @type {Function} */(value)(target, index, total);
// Fallback to 0 if the function returns undefined / NaN / null / false / 0
return !isNaN(+computed) ? +computed : computed || 0;
};
} else if (isStr(value) && stringStartsWith(value, cssVarPrefix)) {
func = () => {
const match = value.match(cssVariableMatchRgx);
const cssVarName = match[1];
const fallbackValue = match[2];
let computed = getComputedStyle(/** @type {HTMLElement} */(target))?.getPropertyValue(cssVarName);
// Use fallback if CSS variable is not set or empty
if ((!computed || computed.trim() === emptyString) && fallbackValue) {
computed = fallbackValue.trim();
}
return computed || 0;
};
} else {
return value;
}
if (store) store.func = func;
return func();
};
/**
* @param {Target} target
* @param {String} prop
* @return {tweenTypes}
*/
const getTweenType = (target, prop) => {
return !target[isDomSymbol] ? tweenTypes.OBJECT :
// Handle SVG attributes
target[isSvgSymbol] && isValidSVGAttribute(target, prop) ? tweenTypes.ATTRIBUTE :
// Handle CSS Transform properties differently than CSS to allow individual animations
validTransforms.includes(prop) || shortTransforms.get(prop) ? tweenTypes.TRANSFORM :
// CSS variables
stringStartsWith(prop, '--') ? tweenTypes.CSS_VAR :
// All other CSS properties
prop in /** @type {DOMTarget} */(target).style ? tweenTypes.CSS :
// Handle other DOM Attributes
prop in target ? tweenTypes.OBJECT :
tweenTypes.ATTRIBUTE;
};
/**
* @param {DOMTarget} target
* @param {String} propName
* @param {Object} animationInlineStyles
* @return {String}
*/
const getCSSValue = (target, propName, animationInlineStyles) => {
const inlineStyles = target.style[propName];
if (inlineStyles && animationInlineStyles) {
animationInlineStyles[propName] = inlineStyles;
}
const value = inlineStyles || getComputedStyle(target[proxyTargetSymbol] || target).getPropertyValue(propName);
return value === 'auto' ? '0' : value;
};
/**
* @param {Target} target
* @param {String} propName
* @param {tweenTypes} [tweenType]
* @param {Object|void} [animationInlineStyles]
* @return {String|Number}
*/
const getOriginalAnimatableValue = (target, propName, tweenType, animationInlineStyles) => {
const type = !isUnd(tweenType) ? tweenType : getTweenType(target, propName);
return type === tweenTypes.OBJECT ? target[propName] || 0 :
type === tweenTypes.ATTRIBUTE ? /** @type {DOMTarget} */(target).getAttribute(propName) :
type === tweenTypes.TRANSFORM ? parseInlineTransforms(/** @type {DOMTarget} */(target), propName, animationInlineStyles) :
type === tweenTypes.CSS_VAR ? getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles).trimStart() :
getCSSValue(/** @type {DOMTarget} */(target), propName, animationInlineStyles);
};
/**
* @param {Number} x
* @param {Number} y
* @param {String} operator
* @return {Number}
*/
const getRelativeValue = (x, y, operator) => {
return operator === '-' ? x - y :
operator === '+' ? x + y :
x * y;
};
/** @return {TweenDecomposedValue} */
const createDecomposedValueTargetObject = () => {
return {
/** @type {valueTypes} */
t: valueTypes.NUMBER,
n: 0,
u: null,
o: null,
d: null,
s: null,
}
};
/**
* @param {String|Number} rawValue
* @param {TweenDecomposedValue} targetObject
* @return {TweenDecomposedValue}
*/
const decomposeRawValue = (rawValue, targetObject) => {
/** @type {valueTypes} */
targetObject.t = valueTypes.NUMBER;
targetObject.n = 0;
targetObject.u = null;
targetObject.o = null;
targetObject.d = null;
targetObject.s = null;
if (!rawValue) return targetObject;
const num = +rawValue;
if (!isNaN(num)) {
// It's a number
targetObject.n = num;
return targetObject;
} else {
// let str = /** @type {String} */(rawValue).trim();
let str = /** @type {String} */(rawValue);
// Parsing operators (+=, -=, *=) manually is much faster than using regex here
if (str[1] === '=') {
targetObject.o = str[0];
str = str.slice(2);
}
// Skip exec regex if the value type is complex or color to avoid long regex backtracking
const unitMatch = str.includes(' ') ? false : unitsExecRgx.exec(str);
if (unitMatch) {
// Has a number and a unit
targetObject.t = valueTypes.UNIT;
targetObject.n = +unitMatch[1];
targetObject.u = unitMatch[2];
return targetObject;
} else if (targetObject.o) {
// Has an operator (+=, -=, *=)
targetObject.n = +str;
return targetObject;
} else if (isCol(str)) {
// Is a color
targetObject.t = valueTypes.COLOR;
targetObject.d = convertColorStringValuesToRgbaArray(str);
return targetObject;
} else {
// Is a more complex string (generally svg coords, calc() or filters CSS values)
const matchedNumbers = str.match(digitWithExponentRgx);
targetObject.t = valueTypes.COMPLEX;
targetObject.d = matchedNumbers ? matchedNumbers.map(Number) : [];
targetObject.s = str.split(digitWithExponentRgx) || [];
return targetObject;
}
}
};
/**
* @param {Tween} tween
* @param {TweenDecomposedValue} targetObject
* @return {TweenDecomposedValue}
*/
const decomposeTweenValue = (tween, targetObject) => {
targetObject.t = tween._valueType;
targetObject.n = tween._toNumber;
targetObject.u = tween._unit;
targetObject.o = null;
targetObject.d = cloneArray(tween._toNumbers);
targetObject.s = cloneArray(tween._strings);
return targetObject;
};
const decomposedOriginalValue = createDecomposedValueTargetObject();
/**
* @param {Tickable} tickable
* @param {Number} time
* @param {Number} muteCallbacks
* @param {Number} internalRender
* @param {tickModes} tickMode
* @return {Number}
*/
const render = (tickable, time, muteCallbacks, internalRender, tickMode) => {
const parent = tickable.parent;
const duration = tickable.duration;
const completed = tickable.completed;
const iterationDuration = tickable.iterationDuration;
const iterationCount = tickable.iterationCount;
const _currentIteration = tickable._currentIteration;
const _loopDelay = tickable._loopDelay;
const _reversed = tickable._reversed;
const _alternate = tickable._alternate;
const _hasChildren = tickable._hasChildren;
const tickableDelay = tickable._delay;
const tickablePrevAbsoluteTime = tickable._currentTime; // TODO: rename ._currentTime to ._absoluteCurrentTime
const tickableEndTime = tickableDelay + iterationDuration;
const tickableAbsoluteTime = time - tickableDelay;
const tickablePrevTime = clamp$1(tickablePrevAbsoluteTime, -tickableDelay, duration);
const tickableCurrentTime = clamp$1(tickableAbsoluteTime, -tickableDelay, duration);
const deltaTime = tickableAbsoluteTime - tickablePrevAbsoluteTime;
const isCurrentTimeAboveZero = tickableCurrentTime > 0;
const isCurrentTimeEqualOrAboveDuration = tickableCurrentTime >= duration;
const isSetter = duration <= minValue;
const forcedTick = tickMode === tickModes.FORCE;
let isOdd = 0;
let iterationElapsedTime = tickableAbsoluteTime;
// Render checks
// Used to also check if the children have rendered in order to trigger the onRender callback on the parent timer
let hasRendered = 0;
// Execute the "expensive" iterations calculations only when necessary
if (iterationCount > 1) {
// bitwise NOT operator seems to be generally faster than Math.floor() across browsers
const currentIteration = ~~(tickableCurrentTime / (iterationDuration + (isCurrentTimeEqualOrAboveDuration ? 0 : _loopDelay)));
tickable._currentIteration = clamp$1(currentIteration, 0, iterationCount);
// Prevent the iteration count to go above the max iterations when reaching the end of the animation
if (isCurrentTimeEqualOrAboveDuration) tickable._currentIteration--;
isOdd = tickable._currentIteration % 2;
iterationElapsedTime = tickableCurrentTime % (iterationDuration + _loopDelay) || 0;
}
// Checks if exactly one of _reversed and (_alternate && isOdd) is true
const isReversed = _reversed ^ (_alternate && isOdd);
const _ease = /** @type {Renderable} */(tickable)._ease;
let iterationTime = isCurrentTimeEqualOrAboveDuration ? isReversed ? 0 : duration : isReversed ? iterationDuration - iterationElapsedTime : iterationElapsedTime;
if (_ease) iterationTime = iterationDuration * _ease(iterationTime / iterationDuration) || 0;
const isRunningBackwards = (parent ? parent.backwards : tickableAbsoluteTime < tickablePrevAbsoluteTime) ? !isReversed : !!isReversed;
tickable._currentTime = tickableAbsoluteTime;
tickable._iterationTime = iterationTime;
tickable.backwards = isRunningBackwards;
if (isCurrentTimeAboveZero && !tickable.began) {
tickable.began = true;
if (!muteCallbacks && !(parent && (isRunningBackwards || !parent.began))) {
tickable.onBegin(/** @type {CallbackArgument} */(tickable));
}
} else if (tickableAbsoluteTime <= 0) {
tickable.began = false;
}
// Only triggers onLoop for tickable without children, otherwise call the the onLoop callback in the tick function
// Make sure to trigger the onLoop before rendering to allow .refresh() to pickup the current values
if (!muteCallbacks && !_hasChildren && isCurrentTimeAboveZero && tickable._currentIteration !== _currentIteration) {
tickable.onLoop(/** @type {CallbackArgument} */(tickable));
}
if (
forcedTick ||
tickMode === tickModes.AUTO && (
time >= tickableDelay && time <= tickableEndTime || // Normal render
time <= tickableDelay && tickablePrevTime > tickableDelay || // Playhead is before the animation start time so make sure the animation is at its initial state
time >= tickableEndTime && tickablePrevTime !== duration // Playhead is after the animation end time so make sure the animation is at its end state
) ||
iterationTime >= tickableEndTime && tickablePrevTime !== duration ||
iterationTime <= tickableDelay && tickablePrevTime > 0 ||
time <= tickablePrevTime && tickablePrevTime === duration && completed || // Force a render if a seek occurs on an completed animation
isCurrentTimeEqualOrAboveDuration && !completed && isSetter // This prevents 0 duration tickables to be skipped
) {
if (isCurrentTimeAboveZero) {
// Trigger onUpdate callback before rendering
tickable.computeDeltaTime(tickablePrevTime);
if (!muteCallbacks) tickable.onBeforeUpdate(/** @type {CallbackArgument} */(tickable));
}
// Start tweens rendering
if (!_hasChildren) {
// Time has jumped more than globals.tickThreshold so consider this tick manual
const forcedRender = forcedTick || (isRunningBackwards ? deltaTime * -1 : deltaTime) >= globals.tickThreshold;
const absoluteTime = tickable._offset + (parent ? parent._offset : 0) + tickableDelay + iterationTime;
// Only Animation can have tweens, Timer returns undefined
let tween = /** @type {Tween} */(/** @type {JSAnimation} */(tickable)._head);
let tweenTarget;
let tweenStyle;
let tweenTargetTransforms;
let tweenTargetTransformsProperties;
let tweenTransformsNeedUpdate = 0;
while (tween) {
const tweenComposition = tween._composition;
const tweenCurrentTime = tween._currentTime;
const tweenChangeDuration = tween._changeDuration;
const tweenAbsEndTime = tween._absoluteStartTime + tween._changeDuration;
const tweenNextRep = tween._nextRep;
const tweenPrevRep = tween._prevRep;
const tweenHasComposition = tweenComposition !== compositionTypes.none;
if ((forcedRender || (
(tweenCurrentTime !== tweenChangeDuration || absoluteTime <= tweenAbsEndTime + (tweenNextRep ? tweenNextRep._delay : 0)) &&
(tweenCurrentTime !== 0 || absoluteTime >= tween._absoluteStartTime)
)) && (!tweenHasComposition || (
!tween._isOverridden &&
(!tween._isOverlapped || absoluteTime <= tweenAbsEndTime) &&
(!tweenNextRep || (tweenNextRep._isOverridden || absoluteTime <= tweenNextRep._absoluteStartTime)) &&
(!tweenPrevRep || (tweenPrevRep._isOverridden || (absoluteTime >= (tweenPrevRep._absoluteStartTime + tweenPrevRep._changeDuration) + tween._delay)))
))
) {
const tweenNewTime = tween._currentTime = clamp$1(iterationTime - tween._startTime, 0, tweenChangeDuration);
const tweenProgress = tween._ease(tweenNewTime / tween._updateDuration);
const tweenModifier = tween._modifier;
const tweenValueType = tween._valueType;
const tweenType = tween._tweenType;
const tweenIsObject = tweenType === tweenTypes.OBJECT;
const tweenIsNumber = tweenValueType === valueTypes.NUMBER;
// Only round the in-between frames values if the final value is a string
const tweenPrecision = (tweenIsNumber && tweenIsObject) || tweenProgress === 0 || tweenProgress === 1 ? -1 : globals.precision;
// Recompose tween value
/** @type {String|Number} */
let value;
/** @type {Number} */
let number;
if (tweenIsNumber) {
value = number = /** @type {Number} */(tweenModifier(round$1(lerp$1(tween._fromNumber, tween._toNumber, tweenProgress), tweenPrecision )));
} else if (tweenValueType === valueTypes.UNIT) {
// R