UNPKG

animejs

Version:

JavaScript animation engine

118 lines (107 loc) 3.98 kB
/** * Anime.js - svg - ESM * @version v4.3.6 * @license MIT * @copyright 2026 - Julian Garnier */ import { proxyTargetSymbol, K } from '../core/consts.js'; import { isFnc, sqrt } from '../core/helpers.js'; import { parseTargets } from '../core/targets.js'; /** * @import { * TargetsParam, * DrawableSVGGeometry, * } from '../types/index.js' */ /** * @param {SVGGeometryElement} [$el] * @return {Number} */ const getScaleFactor = $el => { let scaleFactor = 1; if ($el && $el.getCTM) { const ctm = $el.getCTM(); if (ctm) { const scaleX = sqrt(ctm.a * ctm.a + ctm.b * ctm.b); const scaleY = sqrt(ctm.c * ctm.c + ctm.d * ctm.d); scaleFactor = (scaleX + scaleY) / 2; } } return scaleFactor; }; /** * Creates a proxy that wraps an SVGGeometryElement and adds drawing functionality. * @param {SVGGeometryElement} $el - The SVG element to transform into a drawable * @param {number} start - Starting position (0-1) * @param {number} end - Ending position (0-1) * @return {DrawableSVGGeometry} - Returns a proxy that preserves the original element's type with additional 'draw' attribute functionality */ const createDrawableProxy = ($el, start, end) => { const pathLength = K; const computedStyles = getComputedStyle($el); const strokeLineCap = computedStyles.strokeLinecap; // @ts-ignore const $scalled = computedStyles.vectorEffect === 'non-scaling-stroke' ? $el : null; let currentCap = strokeLineCap; const proxy = new Proxy($el, { get(target, property) { const value = target[property]; if (property === proxyTargetSymbol) return target; if (property === 'setAttribute') { return (...args) => { if (args[0] === 'draw') { const value = args[1]; const values = value.split(' '); const v1 = +values[0]; const v2 = +values[1]; // TOTO: Benchmark if performing two slices is more performant than one split // const spaceIndex = value.indexOf(' '); // const v1 = round(+value.slice(0, spaceIndex), precision); // const v2 = round(+value.slice(spaceIndex + 1), precision); const scaleFactor = getScaleFactor($scalled); const os = v1 * -pathLength * scaleFactor; const d1 = (v2 * pathLength * scaleFactor) + os; const d2 = (pathLength * scaleFactor + ((v1 === 0 && v2 === 1) || (v1 === 1 && v2 === 0) ? 0 : 10 * scaleFactor) - d1); if (strokeLineCap !== 'butt') { const newCap = v1 === v2 ? 'butt' : strokeLineCap; if (currentCap !== newCap) { target.style.strokeLinecap = `${newCap}`; currentCap = newCap; } } target.setAttribute('stroke-dashoffset', `${os}`); target.setAttribute('stroke-dasharray', `${d1} ${d2}`); } return Reflect.apply(value, target, args); }; } if (isFnc(value)) { return (...args) => Reflect.apply(value, target, args); } else { return value; } } }); if ($el.getAttribute('pathLength') !== `${pathLength}`) { $el.setAttribute('pathLength', `${pathLength}`); proxy.setAttribute('draw', `${start} ${end}`); } return /** @type {DrawableSVGGeometry} */(proxy); }; /** * Creates drawable proxies for multiple SVG elements. * @param {TargetsParam} selector - CSS selector, SVG element, or array of elements and selectors * @param {number} [start=0] - Starting position (0-1) * @param {number} [end=0] - Ending position (0-1) * @return {Array<DrawableSVGGeometry>} - Array of proxied elements with drawing functionality */ const createDrawable = (selector, start = 0, end = 0) => { const els = parseTargets(selector); return els.map($el => createDrawableProxy( /** @type {SVGGeometryElement} */($el), start, end )); }; export { createDrawable };