Motion Path
Animate elements along SVG paths with auto-rotation and alignment.
Motion path moves an element along an SVG path during an animation. The element travels from one point on the path to another, optionally rotating to follow the direction of travel.
Basic Usage
Add a path object inside to (or from). Set target to a CSS selector pointing to a <path> element in the DOM.
import { Motion } from "@motion.page/sdk";
Motion("follow", ".dot", {
to: { path: { target: "#track" } },
duration: 2,
ease: "power2.inOut",
}).onPageLoad(); The element moves from the start of the path (0) to the end (1) over the animation duration.
path Config
The path object is set as a property inside from or to.
| Property | Type | Default | Description |
|---|---|---|---|
target | string | Element | — | Required. CSS selector pointing to a DOM <path>, or raw SVG path data (a string starting with M or m) |
align | string | Element | — | Snap the element onto the path by aligning its bounding box to this selector or element. Use "self" to align to the animated element itself |
alignAt | [number, number] | [50, 50] | The origin point on the element that aligns to the path, as [x%, y%] percentages |
start | number | 0 | Path start position as a fraction 0–1 |
end | number | 1 | Path end position as a fraction 0–1 |
rotate | boolean | — | When true, the element auto-rotates to follow the tangent direction of the path |
Targeting a DOM Path
Point target at a <path> element already in the SVG markup. The element is invisible to the user — it only guides the animation.
// HTML: <svg><path id="route" d="M 0 100 C 150 0 300 200 450 100" /></svg>
Motion("car", ".car-icon", {
to: { path: { target: "#route", rotate: true } },
duration: 3,
ease: "power1.inOut",
}).onPageLoad(); Raw SVG Path Data
Pass a path d attribute string directly as target — no DOM element required. The string must start with M or m.
Motion("wave", ".particle", {
to: {
path: {
target: "M 0 200 C 100 100 200 300 300 200 C 400 100 500 300 600 200",
rotate: false,
},
},
duration: 3,
ease: "power2.inOut",
}).play(); Auto-Rotate
Set rotate: true to make the element turn so it always faces along the path’s direction of travel. Essential for arrows, cars, planes, or any element where orientation matters.
Motion("orbit", ".arrow", {
to: { path: { target: "#orbit-path", rotate: true } },
duration: 4,
ease: "linear",
repeat: -1,
}).play(); Align to Path
The align property snaps the element’s bounding box onto the path, so the animation starts exactly where the path begins. Without it, the element may appear to jump at the start of the animation.
Motion("badge", ".badge", {
to: {
path: {
target: "#guide-path",
align: "#guide-path", // Align element origin to path origin
rotate: true,
},
},
duration: 2,
ease: "power2.inOut",
}).onPageLoad(); Use "self" to align relative to the animated element itself:
to: {
path: {
target: "#route",
align: "self",
},
} Origin Point with alignAt
alignAt controls which point on the element is placed on the path. Values are [x%, y%], defaulting to [50, 50] (center). Use [0, 0] to align by the top-left corner, or [50, 100] to align by the bottom-center.
to: {
path: {
target: "#route",
align: "#route",
alignAt: [50, 100], // Anchor at bottom-center of the element
rotate: true,
},
} Partial Path with start and end
Use start and end to animate over only a portion of the path. Both accept fractions from 0 to 1.
// Animate the middle third of the path
Motion("segment", ".dot", {
to: {
path: {
target: "#full-path",
start: 0.33,
end: 0.66,
},
},
duration: 1.5,
ease: "power2.inOut",
}).onPageLoad(); This is useful for step-by-step reveals, progress indicators, or multi-stage animations where different elements cover different segments of the same path.
Scrubbed on Scroll
Combine motion path with .onScroll({ scrub: true }) to drive the element’s position along the path with the scrollbar.
Motion("scroll-path", ".marker", {
to: {
path: {
target: "#timeline-path",
rotate: true,
},
},
duration: 1,
}).onScroll({
scrub: true,
start: "top center",
end: "bottom center",
}); Looping Orbit
Use repeat: -1 with ease: "linear" for a smooth, infinite loop along a closed path.
Motion("orbit", ".satellite", {
to: { path: { target: "#orbit-path", rotate: true } },
duration: 6,
ease: "linear",
repeat: -1,
}).onPageLoad(); For a closed path (one where start and end points meet), "linear" easing ensures there is no speed variation and the loop is seamless.
Multi-Step Timeline
Animate multiple elements along paths in sequence using a multi-step timeline.
Motion("journey", "#map", [
{
target: ".marker",
to: { path: { target: "#segment-a", rotate: true } },
duration: 1.5,
ease: "power2.inOut",
},
{
target: ".marker",
to: { path: { target: "#segment-b", rotate: true } },
duration: 1.5,
ease: "power2.inOut",
position: "+=0.2",
},
]).onPageLoad(); Tips
- The SVG
<path>can be hidden — setfill: none; stroke: none; visibility: hiddenin CSS. It only needs to exist in the DOM for itsdattribute to be read. - Raw path data is coordinate-space dependent — when passing a path string directly, make sure the coordinates match your layout. DOM path selectors are safer because coordinates are resolved from the element’s actual position.
rotate: trueis essential for directional elements — arrows, vehicles, and cursors look wrong without it because they always point the same direction.- Combine with
opacity— fade the element in at the start of a path animation to avoid a sudden appearance by addingfrom: { opacity: 0 }alongsideto: { path: { ... } }using a multi-step timeline. - For pixel-perfect alignment use both
alignandalignAttogether to control exactly which part of the element sits on the path.
Related
- SVG Animations — draw SVG strokes and animate fill and stroke colors
- Scroll Trigger — scrub animations based on scroll position
- Repeat & Yoyo — loop and alternate animations