animejs
Version:
JavaScript animation engine
89 lines (81 loc) • 2.51 kB
JavaScript
/**
* Anime.js - svg - ESM
* @version v4.3.6
* @license MIT
* @copyright 2026 - Julian Garnier
*/
import { isSvgSymbol } from '../core/consts.js';
import { atan2, PI } from '../core/helpers.js';
import { getPath } from './helpers.js';
/**
* @import {
* TargetsParam,
* FunctionValue,
* TweenObjectValue,
* TweenModifier,
* } from '../types/index.js'
*/
// Motion path animation
/**
* @param {SVGGeometryElement} $path
* @param {Number} totalLength
* @param {Number} progress
* @param {Number} lookup
* @param {Boolean} shouldClamp
* @return {DOMPoint}
*/
const getPathPoint = ($path, totalLength, progress, lookup, shouldClamp) => {
const point = progress + lookup;
const pointOnPath = shouldClamp
? Math.max(0, Math.min(point, totalLength)) // Clamp between 0 and totalLength
: (point % totalLength + totalLength) % totalLength; // Wrap around
return $path.getPointAtLength(pointOnPath);
};
/**
* @param {SVGGeometryElement} $path
* @param {String} pathProperty
* @param {Number} [offset=0]
* @return {FunctionValue}
*/
const getPathProgess = ($path, pathProperty, offset = 0) => {
return $el => {
const totalLength = +($path.getTotalLength());
const inSvg = $el[isSvgSymbol];
const ctm = $path.getCTM();
const shouldClamp = offset === 0;
/** @type {TweenObjectValue} */
return {
from: 0,
to: totalLength,
/** @type {TweenModifier} */
modifier: progress => {
const offsetLength = offset * totalLength;
const newProgress = progress + offsetLength;
if (pathProperty === 'a') {
const p0 = getPathPoint($path, totalLength, newProgress, -1, shouldClamp);
const p1 = getPathPoint($path, totalLength, newProgress, 1, shouldClamp);
return atan2(p1.y - p0.y, p1.x - p0.x) * 180 / PI;
} else {
const p = getPathPoint($path, totalLength, newProgress, 0, shouldClamp);
return pathProperty === 'x' ?
inSvg || !ctm ? p.x : p.x * ctm.a + p.y * ctm.c + ctm.e :
inSvg || !ctm ? p.y : p.x * ctm.b + p.y * ctm.d + ctm.f
}
}
}
}
};
/**
* @param {TargetsParam} path
* @param {Number} [offset=0]
*/
const createMotionPath = (path, offset = 0) => {
const $path = getPath(path);
if (!$path) return;
return {
translateX: getPathProgess($path, 'x', offset),
translateY: getPathProgess($path, 'y', offset),
rotate: getPathProgess($path, 'a', offset),
}
};
export { createMotionPath };