Skip to content20cuts
Menu

SlideIn

Move a layer in from a chosen direction, with an optional fade.

MarkdownView source

Installation

Run once from your product repo. The installer includes SlideIn, its source, and the agent skill.

Terminal
npx -y https://20cuts.com/releases/v0.3.0/20cuts-create-0.3.0.tgz video/20cuts

Already installed? Import it in your video project. Full setup guide

Import
import { SlideIn } from "./system/motion";

Usage

The starter already includes SlideInExample in Remotion Studio. To edit a copy, save the Code tab as src/SlideInExample.tsx and register it below.

Register in Remotion

Use the complete example from the Code tab. For a separate entry point, replace src/index.tsx with this registration, then run npm run studio.

src/index.tsx
import { Composition, registerRoot } from "remotion";
import { SlideInExample } from "./SlideInExample";

registerRoot(() => (
  <Composition
    id="SlideInExample"
    component={SlideInExample}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={180}
  />
));

Props

PropTypeDefault
children

React content to display.

ReactNodeRequired
at

Start time in seconds within the current Remotion Sequence.

number0
duration

Animation duration in seconds.

number0.6
from

Direction the content enters from.

"up" | "down" | "left" | "right""up"
distance

Travel distance in pixels.

number40
fade

Fade in while translating.

booleantrue
style

Styles applied to the component wrapper.

CSSProperties
MotionProps
MotionProps type
Source
export type MotionProps = MotionTiming & {children: ReactNode; style?: CSSProperties};
PropTypeDefault
at

Start time in seconds within the current Remotion Sequence.

number
duration

Animation duration in seconds.

number
children

React content to display.

ReactNodeRequired
style

Styles applied to the component wrapper.

CSSProperties
MotionTiming
MotionTiming type
Source
export type MotionTiming = {at?: number; duration?: number};
PropTypeDefault
at

Start time in seconds within the current Remotion Sequence.

number
duration

Animation duration in seconds.

number

Source

Read the component files
motion/effects.tsx
Source
import React, {Children, type CSSProperties, type ReactNode} from "react";
import {useCurrentFrame, useVideoConfig} from "remotion";
import {finite, lerp, nonNegative, phase, positive, reveal, staggerAt, swapState, type MotionTiming, type Point} from "./core.js";

export type MotionProps = MotionTiming & {children: ReactNode; style?: CSSProperties};

export function FadeIn({children, at = 0, duration = 0.6, style}: MotionProps) {
  const p = reveal(useCurrentFrame(), useVideoConfig().fps, at, duration);
  return <div style={{...style, opacity: p}}>{children}</div>;
}

export type SlideInProps = MotionProps & {from?: "up" | "down" | "left" | "right"; distance?: number; fade?: boolean};
export function SlideIn({children, at = 0, duration = 0.6, from = "up", distance = 40, fade = true, style}: SlideInProps) {
  const p = reveal(useCurrentFrame(), useVideoConfig().fps, at, duration);
  nonNegative(distance, "distance");
  if (!["up", "down", "left", "right"].includes(from)) throw new TypeError("from must be up, down, left, or right.");
  const axis = from === "left" || from === "right" ? "X" : "Y";
  const sign = from === "left" || from === "up" ? -1 : 1;
  return <div style={{...style, opacity: fade ? p : 1, transform: `translate${axis}(${sign * distance * (1 - p)}px)`}}>{children}</div>;
}

export type ScaleInProps = MotionProps & {from?: number; origin?: string};
export function ScaleIn({children, at = 0, duration = 0.6, from = 0.88, origin = "50% 50%", style}: ScaleInProps) {
  const p = reveal(useCurrentFrame(), useVideoConfig().fps, at, duration);
  nonNegative(from, "from");
  return <div style={{...style, opacity: p, transform: `scale(${lerp(from, 1, p)})`, transformOrigin: origin}}>{children}</div>;
}

export type AnchoredRevealProps = MotionProps & {anchor: Point; from?: number};
export function AnchoredReveal({children, anchor, from = 0.75, at = 0, duration = 0.6, style}: AnchoredRevealProps) {
  finite(anchor.x, "anchor.x"); finite(anchor.y, "anchor.y");
  return <ScaleIn at={at} duration={duration} from={from} origin={`${anchor.x}px ${anchor.y}px`} style={style}>{children}</ScaleIn>;
}

export type StaggerCascadeProps = MotionProps & {stagger?: number; distance?: number; itemStyle?: CSSProperties};
export function StaggerCascade({children, at = 0, duration = 0.55, stagger = 0.09, distance = 24, style, itemStyle}: StaggerCascadeProps) {
  nonNegative(stagger, "stagger"); nonNegative(distance, "distance"); positive(duration, "duration"); finite(at, "at");
  return <div style={style}>{Children.map(children, (child, i) => <SlideIn key={i} at={staggerAt(at, i, stagger)} duration={duration} from="down" distance={distance} style={itemStyle}>{child}</SlideIn>)}</div>;
}

export type ValueSwapProps = MotionTiming & {from: ReactNode; to: ReactNode; style?: CSSProperties};
/** Both values occupy the same grid cell, reserving the larger value's geometry. */
export function ValueSwap({from, to, at = 0, duration = 0.5, style}: ValueSwapProps) {
  const state = swapState(phase(useCurrentFrame(), useVideoConfig().fps, at, duration));
  return <span style={{display: "inline-grid", ...style}}>
    <span aria-hidden={state.side !== "from"} style={{gridArea: "1 / 1", opacity: state.side === "from" ? state.opacity : 0}}>{from}</span>
    <span aria-hidden={state.side !== "to"} style={{gridArea: "1 / 1", opacity: state.side === "to" ? state.opacity : 0}}>{to}</span>
  </span>;
}