API ReferencePath Animation
PathFollow Advanced
Manual control, stagger animations, and element distribution
Advanced PathFollow features including manual progress control, stagger animations, element distribution, and canvas integration.
Manual Control
Create a PathFollow instance for direct progress manipulation.
PathFollow.create()
import { PathFollow } from 'faster-motion';
const follower = PathFollow.create('#car', {
path: '#road',
autoRotate: true
});
// Update progress directly
follower.setProgress(0.5); // Move to 50%Try it: Slider Control
const follower = PathFollow.create('#element', {
path: '#curve-path',
autoRotate: true
});
slider.addEventListener('input', (e) => {
const progress = e.target.value / 100;
follower.setProgress(progress);
});PathFollowInstance Properties
| Property/Method | Type | Description |
|---|---|---|
element | Element | The target element |
resource | PathResource | The path resource |
progress | number | Current progress (read/write) |
setProgress(value) | void | Set progress and update position |
update() | void | Force position update |
getPosition() | SampleResult | Get current sample without updating |
getOffsetPosition() | Vec2 | Get position with offsets applied |
destroy() | void | Cleanup instance |
Scroll-Linked Animation
Link path progress to scroll position:
const follower = PathFollow.create('#indicator', {
path: '#scroll-path',
autoRotate: false
});
window.addEventListener('scroll', () => {
const scrollProgress = window.scrollY /
(document.body.scrollHeight - window.innerHeight);
follower.setProgress(scrollProgress);
});Drag-to-Follow
Let users drag an element along a path:
const follower = PathFollow.create('#draggable', {
path: '#guide-path'
});
element.addEventListener('drag', (e) => {
// Find closest point on path
const closest = follower.resource.getClosestPoint(e.clientX, e.clientY);
follower.setProgress(closest.progress);
});Stagger Animations
Animate multiple elements along the same path with staggered timing.
PathFollow.stagger()
PathFollow.stagger('.birds', {
path: '#flight-path',
progress: 1,
duration: 3000,
stagger: 200 // 200ms between each bird
});StaggerOptions
| Option | Type | Description |
|---|---|---|
each | number | Delay between elements (ms) |
from | 'start' | 'end' | 'center' | 'edges' | number | Starting position |
grid | [rows, cols] | 2D grid pattern |
// Stagger from center
PathFollow.stagger('.elements', {
path: '#path',
progress: 1,
stagger: { each: 100, from: 'center' }
});
// Reverse order
PathFollow.stagger('.elements', {
path: '#path',
progress: 1,
stagger: { each: 100, from: 'end' }
});Element Distribution
Instantly position elements at evenly-spaced points along a path.
PathFollow.distribute()
const instances = PathFollow.distribute('.dots', {
path: '#circle-path',
mode: 'even', // Evenly spaced
autoRotate: true
});
// Returns array of PathFollowInstance for manual controlTry it: Distributed Elements
// Distribute planets around orbit
const planets = PathFollow.distribute('.planet', {
path: '#orbit',
mode: 'even',
autoRotate: false
});
// Animate all planets together
function animate() {
planets.forEach((p, i) => {
p.progress += 0.001 * (i + 1); // Different speeds
});
requestAnimationFrame(animate);
}
animate();Distribution Modes
| Mode | Description |
|---|---|
'even' | Evenly spaced along entire path |
'start' | Clustered at start of path |
'center' | Clustered at center of path |
'end' | Clustered at end of path |
// Even distribution
PathFollow.distribute('.items', { path: '#path', mode: 'even' });
// All at start, ready to animate
PathFollow.distribute('.items', { path: '#path', mode: 'start' });Canvas PathFollow
For canvas-based animations using .fmtion files.
fmtion PathFollowTrack Schema
interface PathFollowTrack {
id: string;
name?: string;
target: string; // "canvas://object/element-id"
pathRef: string; // "canvas://object/path-id" or "asset:path-id"
autoRotate?: boolean; // Default: true
rotationOffset?: number; // Degrees
loop?: boolean; // Default: true
offsetX?: number; // Pixels along tangent
offsetY?: number; // Pixels perpendicular
enabled?: boolean;
keyframes: PathFollowKeyframe[];
}
interface PathFollowKeyframe {
time: number; // Milliseconds
progress: number; // 0-1
easing?: string;
}Example .fmtion Track
{
"pathFollowTracks": [{
"id": "follow-1",
"target": "canvas://object/rocket",
"pathRef": "canvas://object/flight-path",
"autoRotate": true,
"rotationOffset": -90,
"keyframes": [
{ "time": 0, "progress": 0 },
{ "time": 3000, "progress": 1, "easing": "easeInOut" }
]
}]
}Path References
PathFollow tracks can reference paths in two ways:
// Reference a canvas path object
"pathRef": "canvas://object/my-path"
// Reference a path asset
"pathRef": "asset:flight-path"Combining with TrimPath
Animate an element following a path while the path draws:
import { TrimPath, PathFollow, PathResource } from 'faster-motion';
const path = new PathResource({ path: '#draw-path' });
// Draw the path
TrimPath.to('#draw-path', {
start: 0,
end: 100,
duration: 2000
});
// Element follows the drawing tip
const follower = PathFollow.create('#pencil', {
path,
autoRotate: true,
rotationOffset: -90
});
// Sync with trim progress
TrimPath.to('#draw-path', {
end: 100,
duration: 2000,
onUpdate: (state) => {
follower.setProgress(state.end / 100);
}
});Performance Tips
Share PathResource Instances
// Create once
const sharedPath = new PathResource({ path: '#orbit' });
// Reuse for all elements
document.querySelectorAll('.satellite').forEach(el => {
PathFollow.to(el, { path: sharedPath, duration: 5000 });
});Use distribute() for Static Positioning
// More efficient than individual setProgress calls
PathFollow.distribute('.markers', { path: '#path', mode: 'even' });Cleanup Manual Instances
const follower = PathFollow.create('#element', { path: '#path' });
// When done
follower.destroy();Clear Cache When Done
// After bulk animations complete
PathFollow.clearCache();Common Patterns
Infinite Orbit
PathFollow.to('#satellite', {
path: '#orbit',
progress: [0, 1],
duration: 10000,
ease: 'linear',
repeat: -1,
loop: true
});Train on Tracks
const trackPath = new PathResource({ path: '#railway' });
// Engine
PathFollow.to('#engine', {
path: trackPath,
progress: 1,
duration: 5000
});
// Cars follow with delay
PathFollow.stagger('.train-car', {
path: trackPath,
progress: 1,
duration: 5000,
stagger: -200 // Negative = behind the engine
});Scroll-Reveal Along Path
const follower = PathFollow.create('#reveal-indicator', {
path: '#scroll-guide'
});
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const progress = entry.intersectionRatio;
follower.setProgress(progress);
});
}, { threshold: Array.from({ length: 100 }, (_, i) => i / 100) });
observer.observe(document.querySelector('#content'));TypeScript Support
import {
PathFollow,
PathFollowOptions,
PathFollowInstance,
PathResource,
SampleResult
} from 'faster-motion';
const path: PathResource = new PathResource({ path: '#curve' });
const instance: PathFollowInstance = PathFollow.create('#element', {
path,
autoRotate: true
});
instance.setProgress(0.5);
const position: SampleResult = instance.getPosition();
instance.destroy();
// Distribute returns array of instances
const distributed: PathFollowInstance[] = PathFollow.distribute('.items', {
path,
mode: 'even'
});See Also
- PathFollow - Basic PathFollow usage
- PathResource - Reusable path data
- TrimPath - Animate path stroke visibility
- Timeline - Orchestrating complex animations