animejs
Version:
JavaScript animation engine
305 lines (272 loc) • 8.52 kB
JavaScript
/**
* Anime.js - core - CJS
* @version v4.3.6
* @license MIT
* @copyright 2026 - Julian Garnier
*/
'use strict';
var consts = require('./consts.cjs');
var globals = require('./globals.cjs');
/**
* @import {
* Target,
* DOMTarget,
* } from '../types/index.js'
*/
// Strings
/**
* @param {String} str
* @return {String}
*/
const toLowerCase = str => str.replace(consts.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 => consts.isBrowser && a instanceof SVGElement;
/**@param {any} a @return {Boolean} */
const isHex = a => consts.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] === ')' || !consts.validRgbHslRgx.test(a)));
/**@param {any} a @return {Boolean} */
const isKey = a => !globals.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 = (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 = (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 = (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 = (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 ? consts.maxValue : v === -Infinity ? -consts.maxValue : v;
/**
* Normalizes time value with minimum threshold
*
* @param {Number} v - Time value to normalize
* @return {Number}
*/
const normalizeTime = v => v <= consts.minValue ? consts.minValue : clampInfinity(round(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;
};
exports.PI = PI;
exports._round = _round;
exports.abs = abs;
exports.addChild = addChild;
exports.asin = asin;
exports.atan2 = atan2;
exports.ceil = ceil;
exports.clamp = clamp;
exports.clampInfinity = clampInfinity;
exports.cloneArray = cloneArray;
exports.cos = cos;
exports.exp = exp;
exports.floor = floor;
exports.forEachChildren = forEachChildren;
exports.isArr = isArr;
exports.isCol = isCol;
exports.isFnc = isFnc;
exports.isHex = isHex;
exports.isHsl = isHsl;
exports.isKey = isKey;
exports.isNil = isNil;
exports.isNum = isNum;
exports.isObj = isObj;
exports.isRgb = isRgb;
exports.isStr = isStr;
exports.isSvg = isSvg;
exports.isUnd = isUnd;
exports.isValidSVGAttribute = isValidSVGAttribute;
exports.lerp = lerp;
exports.max = max;
exports.mergeObjects = mergeObjects;
exports.normalizeTime = normalizeTime;
exports.now = now;
exports.parseNumber = parseNumber;
exports.pow = pow;
exports.removeChild = removeChild;
exports.round = round;
exports.sin = sin;
exports.snap = snap;
exports.sqrt = sqrt;
exports.stringStartsWith = stringStartsWith;
exports.toLowerCase = toLowerCase;