FasterMotion
API ReferencePath Animation

PathResource

Create reusable, high-performance path data for animations

PathResource provides a reusable, serializable container for SVG path data with pre-baked sampling for O(log n) performance. Share one PathResource across multiple animations for optimal efficiency.

Overview

PathResource solves a common problem: when animating multiple elements along the same path, or sampling a path repeatedly, recalculating path data is wasteful. PathResource pre-bakes the path into evenly-spaced points, enabling:

  • O(log n) sampling - Binary search instead of iterating
  • Reusability - One resource, many animations
  • Serialization - Save/load for .fmtion export
  • Rich queries - Position, tangent, closest point, slicing

Creating a PathResource

From SVG Path String

import { PathResource } from 'faster-motion';

const path = new PathResource({
  path: 'M 0,100 C 50,0 150,0 200,100 S 350,200 400,100',
  bakeInterval: 5,  // Distance between baked points (default: 5px)
  closed: false     // Auto-detected if not specified
});

console.log(path.length);  // Total path length in pixels

From DOM Element

// From SVG path element
const path = PathResource.fromElement(
  document.querySelector('#my-path'),
  { bakeInterval: 5 }
);

// From selector (returns array for multiple matches)
const paths = PathResource.fromSelector('.flight-path');

Constructor Options

OptionTypeDefaultDescription
pathstring | SVGPathElementRequiredSVG path data or element
bakeIntervalnumber5Distance between baked points in pixels
closedbooleanAutoWhether path is closed (auto-detected from Z command)
idstringAutoUnique identifier (auto-generated if not provided)

Sampling Methods

getPointAt(progress)

Sample the path at a normalized position (0-1).

const path = new PathResource({ path: 'M 0,0 L 100,0 L 100,100' });

// Get point at 50% along path
const point = path.getPointAt(0.5);

console.log(point.x, point.y);      // Position
console.log(point.angle);           // Tangent angle in radians
console.log(point.tangentX);        // Normalized tangent X
console.log(point.tangentY);        // Normalized tangent Y
console.log(point.progress);        // 0.5
console.log(point.distance);        // Absolute distance in pixels

Try it: Path Sampling

const path = new PathResource({
  path: 'M 50,150 C 50,50 350,50 350,150'
});

// Sample at different positions
[0, 0.25, 0.5, 0.75, 1].forEach(progress => {
  const point = path.getPointAt(progress);
  // Draw marker at point.x, point.y
});

getPointAtDistance(distance)

Sample at an absolute distance along the path.

const point = path.getPointAtDistance(50);  // 50 pixels from start

getClosestPoint(x, y)

Find the closest point on the path to any coordinate.

const result = path.getClosestPoint(mouseX, mouseY);

console.log(result.x, result.y);          // Closest point on path
console.log(result.progress);              // 0-1 position on path
console.log(result.distanceToPoint);       // Distance from query point

Try it: Closest Point

const path = new PathResource({
  path: 'M 50,100 Q 200,20 350,100 T 650,100'
});

canvas.addEventListener('mousemove', (e) => {
  const closest = path.getClosestPoint(e.offsetX, e.offsetY);
  // Draw line from mouse to closest.x, closest.y
});

getSamples(count)

Get evenly-spaced samples along the path.

// Get 10 evenly-spaced points
const samples = path.getSamples(10);

samples.forEach(sample => {
  console.log(sample.x, sample.y, sample.angle);
});

getOffsetPoint(progress, offsetX, offsetY)

Get a position offset from the path at a given progress.

// Get point 20px perpendicular to path at 50% progress
const offset = path.getOffsetPoint(0.5, 0, 20);
console.log(offset.x, offset.y);
ParameterDescription
offsetXOffset along the tangent direction (forward/backward)
offsetYOffset perpendicular to tangent (left/right of path)

Path Operations

slice(startProgress, endProgress)

Extract a portion of the path as an SVG path string.

// Get the middle 50% of the path
const middlePath = path.slice(0.25, 0.75);
console.log(middlePath);  // "M 75,50 L 125,50 ..."

Try it: Path Slicing

const path = new PathResource({
  path: 'M 50,100 C 150,20 250,180 350,100'
});

// Animate sliders to change start/end
const slicedPath = path.slice(startValue, endValue);
// Render slicedPath to show visible portion

reverse()

Create a new PathResource with the path direction reversed.

const reversedPath = path.reverse();
const point = reversedPath.getPointAt(0);  // Now at original end

subPath(startProgress, endProgress)

Create a new PathResource from a portion of the path.

// Create PathResource for just the first half
const firstHalf = path.subPath(0, 0.5);
console.log(firstHalf.length);  // Half the original length

Properties

PropertyTypeDescription
idstringUnique identifier
pathDatastringOriginal SVG path data
lengthnumberTotal path length in pixels
boundsBoundsBounding box { x, y, width, height }
closedbooleanWhether path is closed
centerVec2Center point of bounding box
pointCountnumberNumber of baked points
bakedBakedPathInternal baked data (advanced use)
parsedParsedPathParsed structure (advanced use)
const path = new PathResource({
  path: 'M 0,0 L 100,0 L 100,100 L 0,100 Z'
});

console.log(path.length);      // 400 (perimeter of square)
console.log(path.closed);      // true
console.log(path.bounds);      // { x: 0, y: 0, width: 100, height: 100 }
console.log(path.center);      // { x: 50, y: 50 }

Serialization

PathResource can be serialized for storage or .fmtion export.

toJSON()

const json = path.toJSON();
// {
//   id: "path-123",
//   pathData: "M 0,0 L 100,100",
//   closed: false
// }

fromJSON()

const restored = PathResource.fromJSON(json);

Note: Baked data is not serialized - it's regenerated on load to minimize file size.

Sharing Paths

One of PathResource's key benefits is sharing across multiple animations:

// Create once
const orbitPath = new PathResource({
  path: 'M 200,100 A 100,100 0 1,1 200,300 A 100,100 0 1,1 200,100',
  closed: true
});

// Use for multiple planets
PathFollow.to('#mercury', { path: orbitPath, duration: 2000 });
PathFollow.to('#venus', { path: orbitPath, duration: 3500 });
PathFollow.to('#earth', { path: orbitPath, duration: 5000 });

// Also use for path visualization
TrimPath.to('#orbit-ring', { path: orbitPath, end: 100 });

TypeScript Support

import { PathResource, SampleResult, ClosestPointResult, Bounds } from 'faster-motion';

interface PathResourceOptions {
  path: string | SVGPathElement;
  bakeInterval?: number;
  closed?: boolean;
  id?: string;
}

const path: PathResource = new PathResource({
  path: 'M 0,0 L 100,100'
});

const sample: SampleResult = path.getPointAt(0.5);
const closest: ClosestPointResult = path.getClosestPoint(50, 50);
const bounds: Bounds = path.bounds;

See Also

On this page