path-utils
remotionui/path-utilsFreeUtility
SVG path measuring, sampling, auto-fit and waypoint helpers
Install it
npx shadcn@latest add https://remotionui.com/r/path-utils.jsonSource
1import {2 evolvePath,3 getBoundingBox,4 getLength,5 getPointAtLength,6 getTangentAtLength,7} from "@remotion/paths";89export type PathPoint = {10 x: number;11 y: number;12};1314/** Point on a path plus the heading of the curve at that point, in degrees. */15export type PathSample = PathPoint & {16 angle: number;17};1819/**20 * Measuring a path parses its `d` string, which is far too expensive to redo21 * for every path on every frame. Paths are static strings, so results are22 * cached by the string itself.23 */24const lengthCache = new Map<string, number>();25const boxCache = new Map<string, ReturnType<typeof getBoundingBox>>();2627export function pathLength(d: string): number {28 const cached = lengthCache.get(d);29 if (cached !== undefined) {30 return cached;31 }32 const length = getLength(d);33 lengthCache.set(d, length);34 return length;35}3637export function pathBoundingBox(d: string) {38 const cached = boxCache.get(d);39 if (cached !== undefined) {40 return cached;41 }42 const box = getBoundingBox(d);43 boxCache.set(d, box);44 return box;45}4647export function clampProgress(progress: number) {48 return Math.min(1, Math.max(0, progress));49}5051export function getPathDrawStyles(progress: number, path: string) {52 const evolution = evolvePath(clampProgress(progress), path);53 return {54 strokeDasharray: evolution.strokeDasharray,55 strokeDashoffset: evolution.strokeDashoffset,56 };57}5859/**60 * A viewBox that tightly frames the artwork, so callers can hand over any path61 * — a logo exported at the origin or one sitting at x=940 in a 1024 canvas —62 * without hand-computing coordinates. `padding` is in path units and leaves63 * room for the stroke, which straddles the geometry and would otherwise clip.64 */65export function fitViewBox(paths: string | string[], padding = 0): string {66 const list = (Array.isArray(paths) ? paths : [paths]).filter(Boolean);67 const boxes = list.map(pathBoundingBox);6869 if (boxes.length === 0) {70 return "0 0 100 100";71 }7273 const x1 = Math.min(...boxes.map((box) => box.x1));74 const y1 = Math.min(...boxes.map((box) => box.y1));75 const x2 = Math.max(...boxes.map((box) => box.x2));76 const y2 = Math.max(...boxes.map((box) => box.y2));7778 return [79 x1 - padding,80 y1 - padding,81 Math.max(1, x2 - x1 + padding * 2),82 Math.max(1, y2 - y1 + padding * 2),83 ].join(" ");84}8586/**87 * Sample a path by arc length rather than by segment index, so a cursor or a88 * comet head travels at an even speed no matter how the path was authored.89// … truncated
More from remotionui
All 127 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| ai-composer-utilsai-composer-utils | remotionui | Utilities | Free | no deps | |
| audio-viz-utilsaudio-viz-utils | remotionui | Utilities | Free | no deps | |
| caption-utilscaption-utils | remotionui | Utilities | Free | no deps | |
| chart-utilschart-utils | remotionui | Utilities | Free | no deps | |
| code-syntaxcode-syntax | remotionui | Utilities | Free | no deps | |
| layoutlayout | remotionui | Utilities | Free | no deps | |
| map-utilsmap-utils | remotionui | Utilities | Free | no deps | |
| media-utilsmedia-utils | remotionui | Utilities | Free | no deps |
