animejs
Version:
JavaScript animation engine
118 lines (107 loc) • 3.98 kB
JavaScript
/**
* 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 };