FasterMotion
Version 1.0ApiCanvas

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 hidden
  • end: 0.50 → First path 100% visible, second path hidden
  • end: 0.75 → First path 100% visible, second path 50% visible
  • end: 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 curves

MorphablePath 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

On this page