UNPKG

animejs

Version:

JavaScript animation engine

305 lines (272 loc) 8.52 kB
/** * 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;