animejs
Version:
JavaScript animation engine
1,521 lines (1,342 loc) • 393 kB
JavaScript
/**
* Anime.js - ESM bundle
* @version v4.4.1
* @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 {TargetsArray} [targets]
* @param {Tween|null} [prevTween]
* @param {Timeline} [tl]
* @return {T}
*/
/**
* @typedef {Object} StaggerParams
* @property {Number|String} [start]
* @property {Number|'first'|'center'|'last'|'random'|Array.<Number>} [from]
* @property {Boolean} [reversed]
* @property {Array.<Number>|Boolean} [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|TweakRegister} EasingParam */
/** @typedef {(String & {})|EaseStringParamNames|WAAPIEaseStringParamNames|EasingFunction|Spring|TweakRegister} 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]
* @property {Number} [priority]
*/
/**
* @typedef {TimerOptions & TickableCallbacks<Timer>} TimerParams
*/
// Tween types
/**
* @callback FunctionValue
* @param {Target} [target] - The animated target
* @param {Number} [index] - The target index
* @param {TargetsArray} [targets] - The array of all animated targets
* @param {Tween|null} [prevTween] - The previous sibling tween for the same target and property
* @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> | TweakRegister} 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 {DOMTargetsArray} targets - The array of all 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]
*/
/**
* @typedef {Object} ScrambleTextParams
* @property {String|function(Target, Number, TargetsArray): String} [text] - the text to transition to, otherwise uses the original text
* @property {String|function(Target, Number, TargetsArray): String} [chars] - the characters used for scramble; named sets: 'lowercase', 'uppercase', 'numbers', 'symbols', 'braille', 'blocks', 'shades'; range syntax: 'A-Z', 'a-z0-9'; defaults to 'a-zA-Z0-9!%#_'
* @property {EasingParam} [ease] - the easing applied to the scramble animation
* @property {Number|'left'|'center'|'right'|'random'|'auto'} [from] - where the reveal wave starts from, 'auto' (default) uses 'left' when text grows and 'right' when it shrinks
* @property {Boolean} [reversed] - reverses the reveal order, so 'center' reveals from edges inward instead of center outward
* @property {Boolean|Number|String} [cursor] - characters displayed at the leading edge of the reveal wave; true uses '_', a number is a char code, a string is used directly
* @property {Number} [perturbation] - adds random timing offsets to each character's start and end, creating a more organic reveal
* @property {Number} [seed] - a seed for the random number generator to produce reproducible scramble sequences
* @property {Boolean|String} [override] - controls the starting appearance: false shows original text, true scrambles it (default), '' starts from blank, ' ' replaces characters with spaces, a custom string (supports range syntax like 'A-Z') uses its characters as scramble set
* @property {Number} [revealRate] - characters per second entering the active zone; higher values make the reveal wave move faster (default: 60)
* @property {Number} [settleDuration] - time in ms each character spends scrambling before settling into its final glyph (default: 300)
* @property {Number} [settleRate] - how many times per second scramble characters cycle in the active zone (default: 30)
* @property {Number|function(Target, Number, TargetsArray): Number} [duration] - if set to a value greater than 0, overrides the computed duration from interval and settle; if unset or 0, duration is calculated automatically from text length and timing parameters
* @property {Number|function(Target, Number, TargetsArray): Number} [revealDelay] - delay in ms before the reveal wave starts within the scramble animation
* @property {Number|function(Target, Number, TargetsArray): Number} [delay] - delay in ms before the entire scramble animation starts
* @property {function(String, Number): void} [onChange] - callback fired each time a character changes during scramble; receives the current scrambled text and the eased progress (0-1)
*/
// 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}|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 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 = [
'perspective',
'translateX',
'translateY',
'translateZ',
'rotate',
'rotateX',
'rotateY',
'rotateZ',
'scale',
'scaleX',
'scaleY',
'scaleZ',
'skew',
'skewX',
'skewY',
];
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 relativeValuesExecRgx = /(\*=|\+=|-=)/;
const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/;
/**
* @typedef {Object} EditorGlobals
* @property {boolean} showPanel
* @property {boolean} synced
* @property {Function} addAnimation
* @property {Function} addTimeline
* @property {Function} addTimelineChild
* @property {Function} resolveStagger
* @property {Object|null} _head
* @property {Object|null} _tail
*/
/** @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,
/** @type {EditorGlobals|null} */
editor: null,
};
const globalVersions = { version: '4.4.1', 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;
/**
* 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);
const p = 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;
if (inlineTransforms) {
const cachedTransforms = target[transformsSymbol];
let pos = 0;
const len = inlineTransforms.length;
let fullTranslateValue;
while (pos < len) {
// Skip whitespace
while (pos < len && inlineTransforms.charCodeAt(pos) === 32) pos++;
if (pos >= len) break;
// Read function name
const nameStart = pos;
while (pos < len && inlineTransforms.charCodeAt(pos) !== 40) pos++;
if (pos >= len) break;
const name = inlineTransforms.substring(nameStart, pos);
// Scan to closing paren, recording top-level comma positions
let depth = 1;
const valueStart = pos + 1;
let c1 = -1, c2 = -1;
pos++;
while (pos < len && depth > 0) {
const c = inlineTransforms.charCodeAt(pos);
if (c === 40) depth++;
else if (c === 41) depth--;
else if (c === 44 && depth === 1) {
if (c1 === -1) c1 = pos;
else if (c2 === -1) c2 = pos;
}
pos++;
}
const valueEnd = pos - 1;
// Decompose multi-arg functions into individual axis properties
if (name === 'translate' || name === 'translate3d') {
if (c1 === -1) {
cachedTransforms.translateX = inlineTransforms.substring(valueStart, valueEnd).trim();
} else {
cachedTransforms.translateX = inlineTransforms.substring(valueStart, c1).trim();
if (c2 === -1) {
cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
} else {
cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, c2).trim();
cachedTransforms.translateZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
}
}
fullTranslateValue = inlineTransforms.substring(valueStart, valueEnd);
} else if (name === 'scale' || name === 'scale3d') {
if (c1 === -1) {
cachedTransforms.scale = inlineTransforms.substring(valueStart, valueEnd).trim();
} else {
cachedTransforms.scaleX = inlineTransforms.substring(valueStart, c1).trim();
if (c2 === -1) {
cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
} else {
cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, c2).trim();
cachedTransforms.scaleZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
}
}
} else {
cachedTransforms[name] = inlineTransforms.substring(valueStart, valueEnd);
}
}
// Resolve the requested property from the cache
if (propName === 'translate3d' && fullTranslateValue) {
if (animationInlineStyles) animationInlineStyles[propName] = fullTranslateValue;
return fullTranslateValue;
}
const cached = cachedTransforms[propName];
if (!isUnd(cached)) {
if (animationInlineStyles) animationInlineStyles[propName] = cached;
return cached;
}
}
return propName === 'translate3d' ? '0px, 0px, 0px' :
propName === 'rotate3d' ? '0, 0, 0, 0deg' :
stringStartsWith(propName, 'scale') ? '1' :
stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew') ? '0deg' : '0px';
};
/**
* Builds a CSS transform string from the target's cached transform properties.
* Iterates validTransforms in order (perspective > translate > rotate > scale > skew > matrix).
* When adjacent axis properties are all present, emits a shorter shorthand (translateX + translateY -> translate(x, y))
* The index is advanced past consumed properties so they are not emitted twice.
* Properties without a grouping partner (e.g. translateY alone, scaleZ alone) emit individually.
*
* @param {Record<String, String>} props
* @return {String}
*/
const buildTransformString = (props) => {
let str = emptyString;
for (let i = 0, l = validTransforms.length; i < l; i++) {
const key = validTransforms[i];
const val = props[key];
if (val !== undefined) {
// Group translateX with adjacent translateY / translateZ
if (key === 'translateX') {
const next = props.translateY;
if (next !== undefined) {
const next2 = props.translateZ;
if (next2 !== undefined) {
str += `translate3d(${val},${next},${next2}) `;
i += 2;
} else {
str += `translate(${val},${next}) `;
i += 1;
}
continue;
}
}
// Group scaleX with adjacent scaleY / scaleZ (only when standalone scale is absent)
if (key === 'scaleX' && props.scale === undefined) {
const next = props.scaleY;
if (next !== undefined) {
const next2 = props.scaleZ;
if (next2 !== undefined) {
str += `scale3d(${val},${next},${next2}) `;
i += 2;
} else {
str += `scale(${val},${next}) `;
i += 1;
}
continue;
}
}
// All other properties: emit individually using pre-built fragment string
str += `${transformsFragmentStrings[key]}${val}) `;
}
// Preserve non-animatable rotate3d in correct position (after rotateZ, before scale)
if (key === 'rotateZ') {
if (props.rotate3d !== undefined) str += `rotate3d(${props.rotate3d}) `;
}
}
// Preserve non-animatable matrix/matrix3d from inline styles
if (props.matrix !== undefined) str += `matrix(${props.matrix}) `;
if (props.matrix3d !== undefined) str += `matrix3d(${props.matrix3d}) `;
return str;
};
/**
* 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 {TargetsArray} targets
* @param {Object|null} store
* @param {Tween|null} prevTween
* @return {any}
*/
const getFunctionValue = (value, target, index, targets, store, prevTween) => {
let func;
if (isFnc(value)) {
func = () => {
const computed = /** @type {Function} */(value)(target, index, targets, prevTween);
// 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);
if (type === tweenTypes.OBJECT) {
const value = target[propName];
if (value && animationInlineStyles) animationInlineStyles[propName] = value;
return value || 0;
}
if (type === tweenTypes.ATTRIBUTE) {
const value = /** @type {DOMTarget} */(target).getAttribute(propName);
if (value && animationInlineStyles) animationInlineStyles[propName] = value;
return value;
}
return 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 = createDecomposedVal