UNPKG

animejs

Version:

JavaScript animation engine

698 lines (621 loc) 30.6 kB
/** * Anime.js - animation - CJS * @version v4.3.6 * @license MIT * @copyright 2026 - Julian Garnier */ 'use strict'; var consts = require('../core/consts.cjs'); var helpers = require('../core/helpers.cjs'); var globals = require('../core/globals.cjs'); var targets = require('../core/targets.cjs'); var values = require('../core/values.cjs'); var styles = require('../core/styles.cjs'); var units = require('../core/units.cjs'); var parser = require('../easings/eases/parser.cjs'); var timer = require('../timer/timer.cjs'); var composition = require('./composition.cjs'); var additive = require('./additive.cjs'); /** * @import { * Tween, * TweenKeyValue, * TweenParamsOptions, * TweenValues, * DurationKeyframes, * PercentageKeyframes, * AnimationParams, * TweenPropValue, * ArraySyntaxValue, * TargetsParam, * TimerParams, * TweenParamValue, * DOMTarget, * TargetsArray, * Callback, * EasingFunction, * } from '../types/index.js' * * @import { * Timeline, * } from '../timeline/timeline.js' * * @import { * Spring, * } from '../easings/spring/index.js' */ // Defines decomposed values target objects only once and mutate their properties later to avoid GC // TODO: Maybe move the objects creation to values.js and use the decompose function to create the base object const fromTargetObject = values.createDecomposedValueTargetObject(); const toTargetObject = values.createDecomposedValueTargetObject(); const inlineStylesStore = {}; const toFunctionStore = { func: null }; const fromFunctionStore = { func: null }; const keyframesTargetArray = [null]; const fastSetValuesArray = [null, null]; /** @type {TweenKeyValue} */ const keyObjectTarget = { to: null }; let tweenId = 0; let JSAnimationId = 0; let keyframes; /** @type {TweenParamsOptions & TweenValues} */ let key; /** * @param {DurationKeyframes | PercentageKeyframes} keyframes * @param {AnimationParams} parameters * @return {AnimationParams} */ const generateKeyframes = (keyframes, parameters) => { /** @type {AnimationParams} */ const properties = {}; if (helpers.isArr(keyframes)) { const propertyNames = [].concat(.../** @type {DurationKeyframes} */(keyframes).map(key => Object.keys(key))).filter(helpers.isKey); for (let i = 0, l = propertyNames.length; i < l; i++) { const propName = propertyNames[i]; const propArray = /** @type {DurationKeyframes} */(keyframes).map(key => { /** @type {TweenKeyValue} */ const newKey = {}; for (let p in key) { const keyValue = /** @type {TweenPropValue} */(key[p]); if (helpers.isKey(p)) { if (p === propName) { newKey.to = keyValue; } } else { newKey[p] = keyValue; } } return newKey; }); properties[propName] = /** @type {ArraySyntaxValue} */(propArray); } } else { const totalDuration = /** @type {Number} */(values.setValue(parameters.duration, globals.globals.defaults.duration)); const keys = Object.keys(keyframes) .map(key => { return {o: parseFloat(key) / 100, p: keyframes[key]} }) .sort((a, b) => a.o - b.o); keys.forEach(key => { const offset = key.o; const prop = key.p; for (let name in prop) { if (helpers.isKey(name)) { let propArray = /** @type {Array} */(properties[name]); if (!propArray) propArray = properties[name] = []; const duration = offset * totalDuration; let length = propArray.length; let prevKey = propArray[length - 1]; const keyObj = { to: prop[name] }; let durProgress = 0; for (let i = 0; i < length; i++) { durProgress += propArray[i].duration; } if (length === 1) { keyObj.from = prevKey.to; } if (prop.ease) { keyObj.ease = prop.ease; } keyObj.duration = duration - (length ? durProgress : 0); propArray.push(keyObj); } } return key; }); for (let name in properties) { const propArray = /** @type {Array} */(properties[name]); let prevEase; // let durProgress = 0 for (let i = 0, l = propArray.length; i < l; i++) { const prop = propArray[i]; // Emulate WAPPI easing parameter position const currentEase = prop.ease; prop.ease = prevEase ? prevEase : undefined; prevEase = currentEase; // durProgress += prop.duration; // if (i === l - 1 && durProgress !== totalDuration) { // propArray.push({ from: prop.to, ease: prop.ease, duration: totalDuration - durProgress }) // } } if (!propArray[0].duration) { propArray.shift(); } } } return properties; }; class JSAnimation extends timer.Timer { /** * @param {TargetsParam} targets * @param {AnimationParams} parameters * @param {Timeline} [parent] * @param {Number} [parentPosition] * @param {Boolean} [fastSet=false] * @param {Number} [index=0] * @param {Number} [length=0] */ constructor( targets$1, parameters, parent, parentPosition, fastSet = false, index = 0, length = 0 ) { super(/** @type {TimerParams & AnimationParams} */(parameters), parent, parentPosition); ++JSAnimationId; const parsedTargets = targets.registerTargets(targets$1); const targetsLength = parsedTargets.length; // If the parameters object contains a "keyframes" property, convert all the keyframes values to regular properties const kfParams = /** @type {AnimationParams} */(parameters).keyframes; const params = /** @type {AnimationParams} */(kfParams ? helpers.mergeObjects(generateKeyframes(/** @type {DurationKeyframes} */(kfParams), parameters), parameters) : parameters); const { id, delay, duration, ease, playbackEase, modifier, composition: composition$1, onRender, } = params; const animDefaults = parent ? parent.defaults : globals.globals.defaults; const animEase = values.setValue(ease, animDefaults.ease); const animPlaybackEase = values.setValue(playbackEase, animDefaults.playbackEase); const parsedAnimPlaybackEase = animPlaybackEase ? parser.parseEase(animPlaybackEase) : null; const hasSpring = !helpers.isUnd(/** @type {Spring} */(animEase).ease); const tEasing = hasSpring ? /** @type {Spring} */(animEase).ease : values.setValue(ease, parsedAnimPlaybackEase ? 'linear' : animDefaults.ease); const tDuration = hasSpring ? /** @type {Spring} */(animEase).settlingDuration : values.setValue(duration, animDefaults.duration); const tDelay = values.setValue(delay, animDefaults.delay); const tModifier = modifier || animDefaults.modifier; // If no composition is defined and the targets length is high (>= 1000) set the composition to 'none' (0) for faster tween creation const tComposition = helpers.isUnd(composition$1) && targetsLength >= consts.K ? consts.compositionTypes.none : !helpers.isUnd(composition$1) ? composition$1 : animDefaults.composition; // const absoluteOffsetTime = this._offset; const absoluteOffsetTime = this._offset + (parent ? parent._offset : 0); // This allows targeting the current animation in the spring onComplete callback if (hasSpring) /** @type {Spring} */(animEase).parent = this; let iterationDuration = NaN; let iterationDelay = NaN; let animationAnimationLength = 0; let shouldTriggerRender = 0; for (let targetIndex = 0; targetIndex < targetsLength; targetIndex++) { const target = parsedTargets[targetIndex]; const ti = index || targetIndex; const tl = length || targetsLength; let lastTransformGroupIndex = NaN; let lastTransformGroupLength = NaN; for (let p in params) { if (helpers.isKey(p)) { const tweenType = values.getTweenType(target, p); const propName = styles.sanitizePropertyName(p, target, tweenType); let propValue = params[p]; const isPropValueArray = helpers.isArr(propValue); if (fastSet && !isPropValueArray) { fastSetValuesArray[0] = propValue; fastSetValuesArray[1] = propValue; propValue = fastSetValuesArray; } // TODO: Allow nested keyframes inside ObjectValue value (prop: { to: [.5, 1, .75, 2, 3] }) // Normalize property values to valid keyframe syntax: // [x, y] to [{to: [x, y]}] or {to: x} to [{to: x}] or keep keys syntax [{}, {}, {}...] // const keyframes = isArr(propValue) ? propValue.length === 2 && !isObj(propValue[0]) ? [{ to: propValue }] : propValue : [propValue]; if (isPropValueArray) { const arrayLength = /** @type {Array} */(propValue).length; const isNotObjectValue = !helpers.isObj(propValue[0]); // Convert [x, y] to [{to: [x, y]}] if (arrayLength === 2 && isNotObjectValue) { keyObjectTarget.to = /** @type {TweenParamValue} */(/** @type {unknown} */(propValue)); keyframesTargetArray[0] = keyObjectTarget; keyframes = keyframesTargetArray; // Convert [x, y, z] to [[x, y], z] } else if (arrayLength > 2 && isNotObjectValue) { keyframes = []; /** @type {Array.<Number>} */(propValue).forEach((v, i) => { if (!i) { fastSetValuesArray[0] = v; } else if (i === 1) { fastSetValuesArray[1] = v; keyframes.push(fastSetValuesArray); } else { keyframes.push(v); } }); } else { keyframes = /** @type {Array.<TweenKeyValue>} */(propValue); } } else { keyframesTargetArray[0] = propValue; keyframes = keyframesTargetArray; } let siblings = null; let prevTween = null; let firstTweenChangeStartTime = NaN; let lastTweenChangeEndTime = 0; let tweenIndex = 0; for (let l = keyframes.length; tweenIndex < l; tweenIndex++) { const keyframe = keyframes[tweenIndex]; if (helpers.isObj(keyframe)) { key = keyframe; } else { keyObjectTarget.to = /** @type {TweenParamValue} */(keyframe); key = keyObjectTarget; } toFunctionStore.func = null; fromFunctionStore.func = null; const computedToValue = values.getFunctionValue(key.to, target, ti, tl, toFunctionStore); let tweenToValue; // Allows function based values to return an object syntax value ({to: v}) if (helpers.isObj(computedToValue) && !helpers.isUnd(computedToValue.to)) { key = computedToValue; tweenToValue = computedToValue.to; } else { tweenToValue = computedToValue; } const tweenFromValue = values.getFunctionValue(key.from, target, ti, tl); const easeToParse = key.ease || tEasing; const easeFunctionResult = values.getFunctionValue(easeToParse, target, ti, tl); const keyEasing = helpers.isFnc(easeFunctionResult) || helpers.isStr(easeFunctionResult) ? easeFunctionResult : easeToParse; const hasSpring = !helpers.isUnd(keyEasing) && !helpers.isUnd(/** @type {Spring} */(keyEasing).ease); const tweenEasing = hasSpring ? /** @type {Spring} */(keyEasing).ease : keyEasing; // Calculate default individual keyframe duration by dividing the tl of keyframes const tweenDuration = hasSpring ? /** @type {Spring} */(keyEasing).settlingDuration : values.getFunctionValue(values.setValue(key.duration, (l > 1 ? values.getFunctionValue(tDuration, target, ti, tl) / l : tDuration)), target, ti, tl); // Default delay value should only be applied to the first tween const tweenDelay = values.getFunctionValue(values.setValue(key.delay, (!tweenIndex ? tDelay : 0)), target, ti, tl); const computedComposition = values.getFunctionValue(values.setValue(key.composition, tComposition), target, ti, tl); const tweenComposition = helpers.isNum(computedComposition) ? computedComposition : consts.compositionTypes[computedComposition]; // Modifiers are treated differently and don't accept function based value to prevent having to pass a function wrapper const tweenModifier = key.modifier || tModifier; const hasFromvalue = !helpers.isUnd(tweenFromValue); const hasToValue = !helpers.isUnd(tweenToValue); const isFromToArray = helpers.isArr(tweenToValue); const isFromToValue = isFromToArray || (hasFromvalue && hasToValue); const tweenStartTime = prevTween ? lastTweenChangeEndTime + tweenDelay : tweenDelay; // Rounding is necessary here to minimize floating point errors when working in seconds const absoluteStartTime = helpers.round(absoluteOffsetTime + tweenStartTime, 12); // Force a onRender callback if the animation contains at least one from value and autoplay is set to false if (!shouldTriggerRender && (hasFromvalue || isFromToArray)) shouldTriggerRender = 1; let prevSibling = prevTween; if (tweenComposition !== consts.compositionTypes.none) { if (!siblings) siblings = composition.getTweenSiblings(target, propName); let nextSibling = siblings._head; // Iterate trough all the next siblings until we find a sibling with an equal or inferior start time while (nextSibling && !nextSibling._isOverridden && nextSibling._absoluteStartTime <= absoluteStartTime) { prevSibling = nextSibling; nextSibling = nextSibling._nextRep; // Overrides all the next siblings if the next sibling starts at the same time of after as the new tween start time if (nextSibling && nextSibling._absoluteStartTime >= absoluteStartTime) { while (nextSibling) { composition.overrideTween(nextSibling); // This will ends both the current while loop and the upper one once all the next sibllings have been overriden nextSibling = nextSibling._nextRep; } } } } // Decompose values if (isFromToValue) { values.decomposeRawValue(isFromToArray ? values.getFunctionValue(tweenToValue[0], target, ti, tl, fromFunctionStore) : tweenFromValue, fromTargetObject); values.decomposeRawValue(isFromToArray ? values.getFunctionValue(tweenToValue[1], target, ti, tl, toFunctionStore) : tweenToValue, toTargetObject); // Needed to force an inline style registration const originalValue = values.getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore); if (fromTargetObject.t === consts.valueTypes.NUMBER) { if (prevSibling) { if (prevSibling._valueType === consts.valueTypes.UNIT) { fromTargetObject.t = consts.valueTypes.UNIT; fromTargetObject.u = prevSibling._unit; } } else { values.decomposeRawValue( originalValue, values.decomposedOriginalValue ); if (values.decomposedOriginalValue.t === consts.valueTypes.UNIT) { fromTargetObject.t = consts.valueTypes.UNIT; fromTargetObject.u = values.decomposedOriginalValue.u; } } } } else { if (hasToValue) { values.decomposeRawValue(tweenToValue, toTargetObject); } else { if (prevTween) { values.decomposeTweenValue(prevTween, toTargetObject); } else { // No need to get and parse the original value if the tween is part of a timeline and has a previous sibling part of the same timeline values.decomposeRawValue(parent && prevSibling && prevSibling.parent.parent === parent ? prevSibling._value : values.getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore), toTargetObject); } } if (hasFromvalue) { values.decomposeRawValue(tweenFromValue, fromTargetObject); } else { if (prevTween) { values.decomposeTweenValue(prevTween, fromTargetObject); } else { values.decomposeRawValue(parent && prevSibling && prevSibling.parent.parent === parent ? prevSibling._value : // No need to get and parse the original value if the tween is part of a timeline and has a previous sibling part of the same timeline values.getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore), fromTargetObject); } } } // Apply operators if (fromTargetObject.o) { fromTargetObject.n = values.getRelativeValue( !prevSibling ? values.decomposeRawValue( values.getOriginalAnimatableValue(target, propName, tweenType, inlineStylesStore), values.decomposedOriginalValue ).n : prevSibling._toNumber, fromTargetObject.n, fromTargetObject.o ); } if (toTargetObject.o) { toTargetObject.n = values.getRelativeValue(fromTargetObject.n, toTargetObject.n, toTargetObject.o); } // Values omogenisation in cases of type difference between "from" and "to" if (fromTargetObject.t !== toTargetObject.t) { if (fromTargetObject.t === consts.valueTypes.COMPLEX || toTargetObject.t === consts.valueTypes.COMPLEX) { const complexValue = fromTargetObject.t === consts.valueTypes.COMPLEX ? fromTargetObject : toTargetObject; const notComplexValue = fromTargetObject.t === consts.valueTypes.COMPLEX ? toTargetObject : fromTargetObject; notComplexValue.t = consts.valueTypes.COMPLEX; notComplexValue.s = helpers.cloneArray(complexValue.s); notComplexValue.d = complexValue.d.map(() => notComplexValue.n); } else if (fromTargetObject.t === consts.valueTypes.UNIT || toTargetObject.t === consts.valueTypes.UNIT) { const unitValue = fromTargetObject.t === consts.valueTypes.UNIT ? fromTargetObject : toTargetObject; const notUnitValue = fromTargetObject.t === consts.valueTypes.UNIT ? toTargetObject : fromTargetObject; notUnitValue.t = consts.valueTypes.UNIT; notUnitValue.u = unitValue.u; } else if (fromTargetObject.t === consts.valueTypes.COLOR || toTargetObject.t === consts.valueTypes.COLOR) { const colorValue = fromTargetObject.t === consts.valueTypes.COLOR ? fromTargetObject : toTargetObject; const notColorValue = fromTargetObject.t === consts.valueTypes.COLOR ? toTargetObject : fromTargetObject; notColorValue.t = consts.valueTypes.COLOR; notColorValue.s = colorValue.s; notColorValue.d = [0, 0, 0, 1]; } } // Unit conversion if (fromTargetObject.u !== toTargetObject.u) { let valueToConvert = toTargetObject.u ? fromTargetObject : toTargetObject; valueToConvert = units.convertValueUnit(/** @type {DOMTarget} */(target), valueToConvert, toTargetObject.u ? toTargetObject.u : fromTargetObject.u, false); // TODO: // convertValueUnit(target, to.u ? from : to, to.u ? to.u : from.u); } // Fill in non existing complex values if (toTargetObject.d && fromTargetObject.d && (toTargetObject.d.length !== fromTargetObject.d.length)) { const longestValue = fromTargetObject.d.length > toTargetObject.d.length ? fromTargetObject : toTargetObject; const shortestValue = longestValue === fromTargetObject ? toTargetObject : fromTargetObject; // TODO: Check if n should be used instead of 0 for default complex values shortestValue.d = longestValue.d.map((/** @type {Number} */_, /** @type {Number} */i) => helpers.isUnd(shortestValue.d[i]) ? 0 : shortestValue.d[i]); shortestValue.s = helpers.cloneArray(longestValue.s); } // Tween factory // Rounding is necessary here to minimize floating point errors when working in seconds const tweenUpdateDuration = helpers.round(+tweenDuration || consts.minValue, 12); // Copy the value of the iniline style if it exist and imediatly nullify it to prevents false positive on other targets let inlineValue = inlineStylesStore[propName]; if (!helpers.isNil(inlineValue)) inlineStylesStore[propName] = null; /** @type {Tween} */ const tween = { parent: this, id: tweenId++, property: propName, target: target, _value: null, _toFunc: toFunctionStore.func, _fromFunc: fromFunctionStore.func, _ease: parser.parseEase(tweenEasing), _fromNumbers: helpers.cloneArray(fromTargetObject.d), _toNumbers: helpers.cloneArray(toTargetObject.d), _strings: helpers.cloneArray(toTargetObject.s), _fromNumber: fromTargetObject.n, _toNumber: toTargetObject.n, _numbers: helpers.cloneArray(fromTargetObject.d), // For additive tween and animatables _number: fromTargetObject.n, // For additive tween and animatables _unit: toTargetObject.u, _modifier: tweenModifier, _currentTime: 0, _startTime: tweenStartTime, _delay: +tweenDelay, _updateDuration: tweenUpdateDuration, _changeDuration: tweenUpdateDuration, _absoluteStartTime: absoluteStartTime, // NOTE: Investigate bit packing to stores ENUM / BOOL _tweenType: tweenType, _valueType: toTargetObject.t, _composition: tweenComposition, _isOverlapped: 0, _isOverridden: 0, _renderTransforms: 0, _inlineValue: inlineValue, _prevRep: null, // For replaced tween _nextRep: null, // For replaced tween _prevAdd: null, // For additive tween _nextAdd: null, // For additive tween _prev: null, _next: null, }; if (tweenComposition !== consts.compositionTypes.none) { composition.composeTween(tween, siblings); } if (isNaN(firstTweenChangeStartTime)) { firstTweenChangeStartTime = tween._startTime; } // Rounding is necessary here to minimize floating point errors when working in seconds lastTweenChangeEndTime = helpers.round(tweenStartTime + tweenUpdateDuration, 12); prevTween = tween; animationAnimationLength++; helpers.addChild(this, tween); } // Update animation timings with the added tweens properties if (isNaN(iterationDelay) || firstTweenChangeStartTime < iterationDelay) { iterationDelay = firstTweenChangeStartTime; } if (isNaN(iterationDuration) || lastTweenChangeEndTime > iterationDuration) { iterationDuration = lastTweenChangeEndTime; } // TODO: Find a way to inline tween._renderTransforms = 1 here if (tweenType === consts.tweenTypes.TRANSFORM) { lastTransformGroupIndex = animationAnimationLength - tweenIndex; lastTransformGroupLength = animationAnimationLength; } } } // Set _renderTransforms to last transform property to correctly render the transforms list if (!isNaN(lastTransformGroupIndex)) { let i = 0; helpers.forEachChildren(this, (/** @type {Tween} */tween) => { if (i >= lastTransformGroupIndex && i < lastTransformGroupLength) { tween._renderTransforms = 1; if (tween._composition === consts.compositionTypes.blend) { helpers.forEachChildren(additive.additive.animation, (/** @type {Tween} */additiveTween) => { if (additiveTween.id === tween.id) { additiveTween._renderTransforms = 1; } }); } } i++; }); } } if (!targetsLength) { console.warn(`No target found. Make sure the element you're trying to animate is accessible before creating your animation.`); } if (iterationDelay) { helpers.forEachChildren(this, (/** @type {Tween} */tween) => { // If (startTime - delay) equals 0, this means the tween is at the begining of the animation so we need to trim the delay too if (!(tween._startTime - tween._delay)) { tween._delay -= iterationDelay; } tween._startTime -= iterationDelay; }); iterationDuration -= iterationDelay; } else { iterationDelay = 0; } // Prevents iterationDuration to be NaN if no valid animatable props have been provided // Prevents _iterationCount to be NaN if no valid animatable props have been provided if (!iterationDuration) { iterationDuration = consts.minValue; this.iterationCount = 0; } /** @type {TargetsArray} */ this.targets = parsedTargets; /** @type {String|Number} */ this.id = !helpers.isUnd(id) ? id : JSAnimationId; /** @type {Number} */ this.duration = iterationDuration === consts.minValue ? consts.minValue : helpers.clampInfinity(((iterationDuration + this._loopDelay) * this.iterationCount) - this._loopDelay) || consts.minValue; /** @type {Callback<this>} */ this.onRender = onRender || animDefaults.onRender; /** @type {EasingFunction} */ this._ease = parsedAnimPlaybackEase; /** @type {Number} */ this._delay = iterationDelay; // NOTE: I'm keeping delay values separated from offsets in timelines because delays can override previous tweens and it could be confusing to debug a timeline with overridden tweens and no associated visible delays. // this._delay = parent ? 0 : iterationDelay; // this._offset += parent ? iterationDelay : 0; /** @type {Number} */ this.iterationDuration = iterationDuration; if (!this._autoplay && shouldTriggerRender) this.onRender(this); } /** * @param {Number} newDuration * @return {this} */ stretch(newDuration) { const currentDuration = this.duration; if (currentDuration === helpers.normalizeTime(newDuration)) return this; const timeScale = newDuration / currentDuration; // NOTE: Find a better way to handle the stretch of an animation after stretch = 0 helpers.forEachChildren(this, (/** @type {Tween} */tween) => { // Rounding is necessary here to minimize floating point errors tween._updateDuration = helpers.normalizeTime(tween._updateDuration * timeScale); tween._changeDuration = helpers.normalizeTime(tween._changeDuration * timeScale); tween._currentTime *= timeScale; tween._startTime *= timeScale; tween._absoluteStartTime *= timeScale; }); return super.stretch(newDuration); } /** * @return {this} */ refresh() { helpers.forEachChildren(this, (/** @type {Tween} */tween) => { const toFunc = tween._toFunc; const fromFunc = tween._fromFunc; if (toFunc || fromFunc) { if (fromFunc) { values.decomposeRawValue(fromFunc(), fromTargetObject); if (fromTargetObject.u !== tween._unit && tween.target[consts.isDomSymbol]) { units.convertValueUnit(/** @type {DOMTarget} */(tween.target), fromTargetObject, tween._unit, true); } tween._fromNumbers = helpers.cloneArray(fromTargetObject.d); tween._fromNumber = fromTargetObject.n; } else if (toFunc) { // When only toFunc exists, get from value from target values.decomposeRawValue(values.getOriginalAnimatableValue(tween.target, tween.property, tween._tweenType), values.decomposedOriginalValue); tween._fromNumbers = helpers.cloneArray(values.decomposedOriginalValue.d); tween._fromNumber = values.decomposedOriginalValue.n; } if (toFunc) { values.decomposeRawValue(toFunc(), toTargetObject); tween._toNumbers = helpers.cloneArray(toTargetObject.d); tween._strings = helpers.cloneArray(toTargetObject.s); // Make sure to apply relative operators https://github.com/juliangarnier/anime/issues/1025 tween._toNumber = toTargetObject.o ? values.getRelativeValue(tween._fromNumber, toTargetObject.n, toTargetObject.o) : toTargetObject.n; } } }); // This forces setter animations to render once if (this.duration === consts.minValue) this.restart(); return this; } /** * Cancel the animation and revert all the values affected by this animation to their original state * @return {this} */ revert() { super.revert(); return styles.cleanInlineStyles(this); } /** * @typedef {this & {then: null}} ResolvedJSAnimation */ /** * @param {Callback<ResolvedJSAnimation>} [callback] * @return Promise<this> */ then(callback) { return super.then(callback); } } /** * @param {TargetsParam} targets * @param {AnimationParams} parameters * @return {JSAnimation} */ const animate = (targets, parameters) => new JSAnimation(targets, parameters, null, 0, false).init(); exports.JSAnimation = JSAnimation; exports.animate = animate;