TrimPath
Animate path stroke visibility with start, end, and offset controls
TrimPath provides After Effects-style path trimming for canvas animations. Control which portion of a path's stroke is visible by animating start, end, and offset values.
Overview
TrimPath works by manipulating the visible portion of a path's stroke using dash arrays. This creates effects like:
- Draw On - Path draws from start to end
- Draw Off - Path erases from start to end
- Reveal from Center - Path reveals outward from middle
- Sequential Mode - Multiple paths animate as one continuous path
Modes
Parallel Mode (Default)
Each path is trimmed independently using its own length. All paths animate simultaneously.
// In .fmtion format
{
"trimPathTracks": [{
"target": "canvas://path1",
"simultaneous": true, // Parallel mode (default)
"keyframes": [
{ "time": 0, "start": 0, "end": 0, "offset": 0 },
{ "time": 1000, "start": 0, "end": 1, "offset": 0 }
]
}]
}Sequential Mode
Multiple paths are treated as one continuous path. Global trim values are distributed across paths based on their relative lengths.
// In .fmtion format
{
"trimPathTracks": [
{
"target": "canvas://path1",
"simultaneous": false, // Sequential mode
"keyframes": [
{ "time": 0, "start": 0, "end": 0, "offset": 0 },
{ "time": 2000, "start": 0, "end": 1, "offset": 0 }
]
},
{
"target": "canvas://path2",
"simultaneous": false,
"keyframes": [
{ "time": 0, "start": 0, "end": 0, "offset": 0 },
{ "time": 2000, "start": 0, "end": 1, "offset": 0 }
]
}
]
}How Sequential Mode Works:
With 2 paths of equal length (100px each, total 200px):
end: 0.25→ First path 50% visible, second path hiddenend: 0.50→ First path 100% visible, second path hiddenend: 0.75→ First path 100% visible, second path 50% visibleend: 1.00→ Both paths fully visible
This matches Lottie's m property behavior:
m=1→ Parallel (simultaneous)m=2→ Sequential
TrimPath Utilities
FasterMotion exports utility functions for calculating sequential trim values. These are used internally by the runtime and can be used by editors for preview.
calculateSequentialPathInfo
Builds path info array with cumulative offsets.
import { calculateSequentialPathInfo } from 'faster-motion';
const pathLengths = [
{ id: 'path1', length: 100 },
{ id: 'path2', length: 150 },
{ id: 'path3', length: 50 }
];
const pathInfos = calculateSequentialPathInfo(pathLengths);
// Returns:
// [
// { id: 'path1', length: 100, startOffset: 0 }, // 0-33%
// { id: 'path2', length: 150, startOffset: 0.333 }, // 33-83%
// { id: 'path3', length: 50, startOffset: 0.833 } // 83-100%
// ]calculateSequentialTrimValues
Distributes global trim values to individual paths.
import {
calculateSequentialPathInfo,
calculateSequentialTrimValues
} from 'faster-motion';
const pathInfos = calculateSequentialPathInfo(pathLengths);
// Global trim: show 25% to 75% of combined path
const trimValues = calculateSequentialTrimValues(
0.25, // globalStart
0.75, // globalEnd
0, // globalOffset
pathInfos
);
// Returns Map<string, { localStart: number, localEnd: number }>
trimValues.get('path1'); // { localStart: 0.75, localEnd: 1.0 }
trimValues.get('path2'); // { localStart: 0, localEnd: 0.83 }
trimValues.get('path3'); // { localStart: 0, localEnd: 0 }calculateLocalTrim
Calculate local trim for a single path segment.
import { calculateLocalTrim } from 'faster-motion';
const { localStart, localEnd } = calculateLocalTrim(
0.25, // globalStart
0.75, // globalEnd
0.333, // pathStartOffset (where this path starts in sequence)
0.833 // pathEndOffset (where this path ends in sequence)
);calculatePathLengthFromPoints
Calculate path length from an array of path points.
import { calculatePathLengthFromPoints } from 'faster-motion';
const points = [
{ x: 0, y: 0 },
{ x: 100, y: 0, cx1: 50, cy1: -50, cx2: 50, cy2: 50 },
{ x: 100, y: 100 }
];
const length = calculatePathLengthFromPoints(points, false);
// Returns approximate path length including bezier curvesMorphablePath Properties
Canvas MorphablePath objects support trim properties directly:
// Properties (0-1 values)
path.trimStart = 0; // Start of visible portion
path.trimEnd = 1; // End of visible portion
path.trimOffset = 0; // Rotation offset
// Example: Show middle 50%
path.trimStart = 0.25;
path.trimEnd = 0.75;These properties are animated by the fmtion loader when playing TrimPath tracks.
fmtion Schema
TrimPathTrack
interface TrimPathTrack {
id: string;
target: string; // "canvas://objectId"
enabled?: boolean;
simultaneous?: boolean; // true=Parallel (default), false=Sequential
keyframes: TrimPathKeyframe[];
}
interface TrimPathKeyframe {
time: number; // Time in milliseconds
start: number; // 0-1, start of visible portion
end: number; // 0-1, end of visible portion
offset: number; // 0-1, rotation offset
easing?: string; // Easing function name
}Examples
Draw On Effect
// Path draws from nothing to fully visible
const animation = {
trimPathTracks: [{
target: "canvas://myPath",
keyframes: [
{ time: 0, start: 0, end: 0, offset: 0 },
{ time: 1000, start: 0, end: 1, offset: 0, easing: "easeOut" }
]
}]
};Draw Off Effect
// Path erases from start
const animation = {
trimPathTracks: [{
target: "canvas://myPath",
keyframes: [
{ time: 0, start: 0, end: 1, offset: 0 },
{ time: 1000, start: 1, end: 1, offset: 0, easing: "easeIn" }
]
}]
};Reveal from Center
// Path reveals outward from center
const animation = {
trimPathTracks: [{
target: "canvas://myPath",
keyframes: [
{ time: 0, start: 0.5, end: 0.5, offset: 0 },
{ time: 1000, start: 0, end: 1, offset: 0, easing: "easeInOut" }
]
}]
};Sequential Multi-Path
// Multiple paths draw on sequentially
const animation = {
trimPathTracks: [
{
target: "canvas://path1",
simultaneous: false,
keyframes: [
{ time: 0, start: 0, end: 0, offset: 0 },
{ time: 2000, start: 0, end: 1, offset: 0 }
]
},
{
target: "canvas://path2",
simultaneous: false,
keyframes: [
{ time: 0, start: 0, end: 0, offset: 0 },
{ time: 2000, start: 0, end: 1, offset: 0 }
]
}
]
};See Also
- MorphablePath - Canvas path rendering
- Timeline - Sequencing animations
- Easing - Easing functions