motion-primitive
remotionui/motion-primitiveFreeUtility
Enter/exit motion contract shared by the animation primitives — opacity leads, exits are shorter, and a bounded Sequence times the exit itself
Install it
npx shadcn@latest add https://remotionui.com/r/motion-primitive.jsonSource
1import type { CSSProperties, ReactNode } from "react";2import {3 Easing,4 interpolate,5 spring,6 useCurrentFrame,7 useVideoConfig,8 type SpringConfig,9} from "remotion";10import { springBouncy, springSmooth, springSnappy } from "./springs";11import { EASING_ENTER, EASING_EXIT } from "./timing";1213/**14 * One motion contract behind every enter/exit primitive.15 *16 * Two rules do most of the work:17 *18 * 1. **Light leads the move.** Opacity finishes at ~55% of an entrance, so the19 * last third of the travel happens on a fully opaque element. Cross-fading20 * for the whole move is what makes an animation read as a dissolve rather21 * than an arrival.22 * 2. **An exit is not an entrance played backwards.** It is shorter (70% of the23 * enter duration), it travels less (60% of the distance), and it accelerates24 * away instead of decelerating into place.25 *26 * Exits are also automatic: inside a `<Sequence durationInFrames={n}>`,27 * `useVideoConfig().durationInFrames` is that window, so `exit` alone is enough28 * to land the element out exactly at the end of its slot.29 */3031export type SpringPreset = "smooth" | "snappy" | "bouncy";3233/** `true` picks the snappy preset; an object overrides fields on it. */34export type MotionSpring = boolean | SpringPreset | Partial<SpringConfig>;3536const SPRING_PRESETS: Record<SpringPreset, SpringConfig> = {37 smooth: springSmooth,38 snappy: springSnappy,39 bouncy: springBouncy,40};4142export function resolveSpringConfig(value: MotionSpring): SpringConfig {43 if (value === true || value === false) return springSnappy;44 if (typeof value === "string") return SPRING_PRESETS[value] ?? springSnappy;45 return { ...springSnappy, ...value };46}4748/**49 * Light snaps, geometry glides.50 *51 * `EASING_ENTER` is an expo-out: it is ~90% resolved a third of the way in,52 * which is right for opacity but throws away most of a transform — a hinge or53 * a defocus is over before the element is even visible. The transform channel54 * decelerates on a cubic instead, so the stated duration is the duration the55 * eye actually sees.56 */57const EASING_ARRIVE = Easing.bezier(0.33, 1, 0.68, 1);5859/** Opacity is complete this far into an entrance. */60const ENTER_OPACITY_LEAD = 0.55;61/** Opacity is gone this far into an exit — the tail of the travel is unseen. */62const EXIT_OPACITY_LEAD = 0.7;63/** Exit duration as a share of the enter duration, when not given. */64const EXIT_DURATION_RATIO = 0.7;65const MIN_EXIT_FRAMES = 6;66// … 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 |
