FasterMotion
API ReferencePath Animation

PathFollow

Animate elements along SVG paths with high-performance sampling

PathFollow animates elements along SVG paths with pre-baked sampling for optimal performance. Elements can auto-rotate to follow the path direction and be offset perpendicular to the path.

Overview

PathFollow uses PathResource internally for O(log n) path sampling, making it significantly faster than traditional motion path implementations. Key features:

  • Progress-based animation - Animate from 0-1 along the path
  • Auto-rotation - Elements rotate to follow path direction
  • Perpendicular offsets - Position elements beside the path
  • Manual control - Set progress directly for scroll/slider linking

Basic Usage

PathFollow.to()

Animate an element along a path.

import { PathFollow } from 'faster-motion';

PathFollow.to('#rocket', {
  path: '#flight-path',
  progress: 1,           // Animate from 0 to 1
  duration: 3000,
  autoRotate: true
});

Try it: Basic Path Following

PathFollow.to('#circle', {
  path: '#curve-path',
  progress: 1,
  duration: 2000,
  ease: 'easeInOut'
});

Progress Range

Animate between specific progress values:

// Animate from 25% to 75% of path
PathFollow.to('#element', {
  path: '#my-path',
  progress: [0.25, 0.75],
  duration: 2000
});

Path Sources

PathFollow accepts paths in multiple formats:

SVG Path String

PathFollow.to('#element', {
  path: 'M 0,100 C 100,0 200,200 300,100',
  duration: 2000
});

DOM Element or Selector

// CSS selector
PathFollow.to('#rocket', {
  path: '#flight-path',
  duration: 2000
});

// DOM element
const pathEl = document.querySelector('#my-path');
PathFollow.to('#rocket', {
  path: pathEl,
  duration: 2000
});

PathResource Instance

For best performance when reusing paths:

import { PathResource, PathFollow } from 'faster-motion';

// Create once, reuse many times
const orbit = new PathResource({
  path: '#orbit-path',
  bakeInterval: 5
});

PathFollow.to('#planet1', { path: orbit, duration: 5000 });
PathFollow.to('#planet2', { path: orbit, duration: 8000 });
PathFollow.to('#planet3', { path: orbit, duration: 12000 });

Auto-Rotation

autoRotate

When enabled (default), elements rotate to follow the path direction.

PathFollow.to('#arrow', {
  path: '#curved-path',
  autoRotate: true,    // Default
  duration: 2000
});

Try it: Auto-Rotate

PathFollow.to('#rocket', {
  path: '#flight-path',
  progress: 1,
  autoRotate: true,
  rotationOffset: -90,  // Point nose forward
  duration: 3000,
  ease: 'power2.inOut'
});

rotationOffset

Add additional rotation after auto-rotate is applied.

PathFollow.to('#rocket', {
  path: '#path',
  autoRotate: true,
  rotationOffset: -90,  // Rotate 90° counter-clockwise
  duration: 2000
});

Common values:

  • 0 - Element's right side points forward
  • -90 - Element's top points forward (useful for arrows/rockets)
  • 90 - Element's bottom points forward
  • 180 - Element's left side points forward

Perpendicular Offsets

Position elements beside the path rather than on it.

offsetX (Along Tangent)

Offset in the direction of travel.

PathFollow.to('#element', {
  path: '#path',
  offsetX: 20,  // 20px ahead of path position
  duration: 2000
});

offsetY (Perpendicular)

Offset perpendicular to the path direction.

PathFollow.to('#element', {
  path: '#path',
  offsetY: 30,   // 30px to the left of path
  duration: 2000
});

PathFollow.to('#element2', {
  path: '#path',
  offsetY: -30,  // 30px to the right of path
  duration: 2000
});

Offset Visualization:

       ↑ offsetY (positive = left)
       |
  ←----●----→ offsetX (positive = forward)
       |
       ↓ offsetY (negative = right)

  ════════════► Path direction

Looping

loop

Control behavior when progress goes beyond 0-1 range.

// Continuous orbit
PathFollow.to('#satellite', {
  path: '#orbit',
  progress: [0, 3],  // Go around 3 times
  loop: true,        // Wrap around at ends
  duration: 10000
});

When loop: true:

  • Progress 1.5 → position at 0.5
  • Progress -0.25 → position at 0.75

When loop: false (default for non-closed paths):

  • Progress clamps to 0-1 range

Animation Options

OptionTypeDefaultDescription
pathPathResource | string | ElementRequiredPath to follow
progressnumber | [start, end]1Target progress (0-1)
progressRationumber-Alias for progress
autoRotatebooleantrueRotate with path direction
rotationOffsetnumber0Additional rotation (degrees)
loopbooleantrueWrap at path boundaries
offsetXnumber0Tangent offset (pixels)
offsetYnumber0Perpendicular offset (pixels)
durationnumber1000Animation duration (ms)
easestring'power2.inOut'Easing function
delaynumber0Start delay (ms)
repeatnumber0Repeat count (-1 = infinite)
yoyobooleanfalseReverse on repeat
onUpdate(progress, sample) => void-Called each frame
onComplete() => void-Called when complete

Getting Path Points

Query path positions without animating an element:

const point = PathFollow.getPointAt('#path', 0.5);

console.log(point.x, point.y);    // Position
console.log(point.angle);         // Tangent angle (radians)
console.log(point.tangentX);      // Normalized tangent X
console.log(point.tangentY);      // Normalized tangent Y

Orbit Animation Example

Try it: Planetary Orbit

const orbit = new PathResource({
  path: '#orbit-ellipse',
  closed: true
});

// Multiple planets at different speeds
PathFollow.to('#mercury', {
  path: orbit,
  progress: [0, 1],
  duration: 2000,
  repeat: -1,
  ease: 'linear'
});

PathFollow.to('#earth', {
  path: orbit,
  progress: [0.33, 1.33],  // Start offset
  duration: 5000,
  repeat: -1,
  ease: 'linear'
});

Shorthand Function

import { pathFollow } from 'faster-motion';

// Shorthand for PathFollow.to()
pathFollow('#element', {
  path: '#my-path',
  duration: 2000
});

TypeScript Support

import { PathFollow, PathFollowOptions, SampleResult, PathResource } from 'faster-motion';

const options: PathFollowOptions = {
  path: '#flight-path',
  progress: 1,
  autoRotate: true,
  rotationOffset: -90,
  duration: 3000,
  onUpdate: (progress: number, sample: SampleResult) => {
    console.log(`At ${progress * 100}%: (${sample.x}, ${sample.y})`);
  }
};

PathFollow.to('#rocket', options);

See Also

On this page