Communitygithub.com

Guilhermeslvg/video-portfolio

Bewährte Methoden für Inhalte, Animationen und Effekte

Was ist video-portfolio?

Dieses Skill bietet eine umfassende Anleitung zur Erstellung von Remotion React Markup mit Fokus auf bewährte Methoden für Inhalte, Animationen und Effekte. Es richtet sich an Entwickler, die interaktive Videokompositionen mit Remotion erstellen möchten, und wird von Plattformen wie Claude Code, Cursor und Codex unterstützt. Das Skill beschreibt, wie Animationen mit `useCurrentFrame()` und `interpolate()` gesteuert werden, anstatt CSS-Transitions oder -Animationen zu verwenden, die nicht korrekt rendern. Es behandelt die Strukturierung von Markup gemäß den Remotion Interactivity Best Practices, einschließlich der Verwendung von `Interactive.withSchema({wrapInSequence: true})` für editierbare Szenen, sowie die korrekte Platzierung von Timing-Eigenschaften und die Verwendung von `premountFor`, um eine reibungslose Wiedergabe zu gewährleisten. Zusätzlich enthält es Hinweise zu Assets, Medienkomponenten, Beispielszenen, Verzögerung und Zuschnitt, sowie zu erweiterten Themen wie Karten, Texthervorhebungen, Übergänge, Bewegungsunschärfe und visuelle Effekte über das `effects`-System.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Guilhermeslvg/video-portfolio/tree/HEAD/.agents/skills/remotion-markup

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Vorschau

Aus dem Skill-README

Dokumentation

Dies ist eine Anleitung für das Schreiben von Remotion React Markup. Wenn dies nicht relevant ist, lade stattdessen die Remotion Best Practices.

Benutzeränderungen bewahren

Benutzer können außerhalb des Gesprächs Code-Änderungen vornehmen.

Wenn du zwischenzeitlich eine überraschende Änderung bemerkst, überschreibe sie nicht, sondern nimm an, sie sei beabsichtigt, oder frage nach Bestätigung.

Allgemeine Regeln

Innerhalb einer Composition oder eines Szenen-Komponenten erhältst du fps aus useVideoConfig() für alle Sekunden-zu-Frames-Berechnungen, einschließlich Timing-Props wie from, trimBefore, durationInFrames und premountFor. Wenn der Ausdruck auf dem JSX-Knoten bleibt, z. B. trimBefore={4 * fps}, kann das Studio das Timing bearbeiten und es bleibt korrekt, wenn sich die Bildrate der Composition ändert.

Steuere Animationen mit useCurrentFrame() und interpolate(). CSS transition oder animation werden nicht korrekt gerendert und müssen umgestaltet werden. Tailwind-Animation-Klassen werden nicht korrekt gerendert und müssen umgestaltet werden.

Verwende Easing.bezier() und Easing.spring(), um das Timing anzupassen.

Strukturiere dein Markup gemäß den Remotion Interactivity Best Practices. Bevorzuge Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) für Szenen mit editierbaren Props. Deren inneres Markup kann ein Fragment zurückgeben; der Wrapper übernimmt Layout und Instanz-Styles. Dimensionierte Elemente wie Lower Thirds können das Standard-Layout behalten und style an ihre eigene Wurzel weiterleiten. Registriere wiederverwendbare Szenen als verbundene Compositions. Setze Timing direkt auf Komponenten, die es unterstützen; vermeide redundante <Sequence>-Wrapper. Gib jeder zeitgesteuerten Komponente, die premountFor unterstützt, eine Sekunde Vorlauf: premountFor={fps}, wobei fps aus useVideoConfig() stammt. Wende dies auf Medien, interaktive Komponenten, <Sequence>, <TransitionSeries.Sequence>, und <TransitionSeries.Overlay> an, einschließlich zeitgesteuerter Komponenten innerhalb von Szenen. Lade auch das übergeordnete Timeline-Element vor, wenn ein verschachteltes Element gemountet werden muss, bevor das übergeordnete startet. Eine Komponente ohne premountFor, wie <TransitionSeries.Transition>, benötigt keinen Ersatz.

Das Studio bearbeitet den JSX-Quellknoten, der ein Element erstellt hat. Verfasse jede Composition-Registrierung, jeden Clip, jede Szene, jede Ebene und jede Sequenz, die unabhängig editierbar sein soll, als eigenen JSX-Knoten, mit ihren editierbaren Props inline. Programmatische Schleifen sind geeignet, wenn die generierten Instanzen absichtlich als eine Quellvorlage gesteuert werden, nicht wenn Benutzer die Instanzen separat bearbeiten müssen.

import { useCurrentFrame, useVideoConfig, Easing, interpolate, Interactive } from "remotion";

export const FadeIn = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  return (
    <Interactive.Div
      name="Title"
      premountFor={fps}
      style={{
        opacity: interpolate(frame, [0, 2 * fps], [0, 1], {
          extrapolateRight: "clamp",
          extrapolateLeft: "clamp",
          easing: Easing.bezier(0.16, 1, 0.3, 1),
        }),
      }}
    >
      Hello World!
    </Interactive.Div>
  );
};

Behalte den interpolate()-Aufruf inline im style-Prop. Verwende scale, translate, rotate CSS-Eigenschaften gegenüber transform.

// 👍 Inline editierbare Keyframes und Transform-Kurzformen
style={{
  scale: interpolate(frame, [0, 100], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
    output: 'perceptual-scale' // Für `scale`-Animationen "output: 'perceptual-scale'" verwenden
  }),
  translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
  rotate: interpolate(frame, [0, 100], ["20deg", "90deg"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
}}

// 👎 Nicht-inline Werte und Transform-Strings sind im Studio schwerer zu bearbeiten
const scale = interpolate(frame, [0, 100], [0, 1]);

style={{
  transform: `scale(${scale})`,
}}

Assets

Lege Assets im public/-Ordner im Projektstammverzeichnis ab. Verwende staticFile(), um Dateien aus dem public/-Ordner zu referenzieren.

Medienkomponenten

Füge Video und Audio mit <Video> und <Audio> aus @remotion/media hinzu. Füge Bilder mit der <CanvasImage>-Komponente hinzu. Füge animierte GIFs, APNG, WebP oder AVIF-Bilder mit <AnimatedImage> hinzu, verwende @remotion/gif, falls nicht Chrome verwendet wird. Verwende staticFile() für Dateien in public/ oder übergib eine Remote-URL direkt:

import { Audio, Video } from "@remotion/media";
import { staticFile, CanvasImage, AnimatedImage, useVideoConfig } from "remotion";

export const MyComposition = () => {
  const {fps} = useVideoConfig();

  return (
    <>
      <Video src={staticFile("video.mp4")} premountFor={fps} style={{ opacity: 0.5 }} />
      <Audio src={staticFile("audio.mp3")} premountFor={fps} />
      <CanvasImage
        src={staticFile("logo.png")}
        premountFor={fps}
        style={{ width: 100, height: 100 }}
      />
      <Video src="https://remotion.media/video.mp4" premountFor={fps} />
      <AnimatedImage src={staticFile('nyancat.gif')} premountFor={fps} />
    </>
  );
};

Wenn die Composition hauptsächlich aus einer Timeline von Video- oder Audioclips besteht, lies video-editing.md, bevor du die Quellstruktur wählst.

Beispielszene

Ein Hintergrundvideo mit einem Lower Third. Der Lower Third ist eine interaktive Komponente mit eigener Timeline, registriert als verbundene Composition. Sein Text wird als children übergeben und accentColor ist ein editierbares Prop. Die Einblendung wird inline am Aufrufort keyframed.

// MyScene.tsx
import { Video } from "@remotion/media";
import { Easing, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
import { LowerThird } from "./LowerThird";

export const MyScene = () => {
  const { fps } = useVideoConfig();
  const frame = useCurrentFrame();

  return (
    <>
      <Video
        name="Background"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{
          position: "absolute",
          width: "100%",
          height: "100%",
        }}
      />
      <LowerThird
        name="Lower third"
        from={1 * fps}
        premountFor={fps}
        accentColor="#0b84f3"
        style={{
          opacity: interpolate(frame, [1 * fps, 2 * fps], [0, 1], {
            extrapolateRight: "clamp",
            extrapolateLeft: "clamp",
            easing: Easing.bezier(0.16, 1, 0.3, 1),
          }),
        }}
      >
        Jane Doe, Product Designer
      </LowerThird>
    </>
  );
};
// LowerThird.tsx
import type React from "react";
import { Interactive, type InteractivitySchema } from "remotion";

type LowerThirdProps = {
  readonly children: React.ReactNode;
  readonly accentColor: string;
  readonly style?: React.CSSProperties;
};

const LowerThirdInner: React.FC<LowerThirdProps> = ({
  children,
  accentColor,
  style,
}) => {
  return (
    <Interactive.Div
      style={{
        position: "absolute",
        left: 80,
        bottom: 80,
        display: "flex",
        alignItems: "center",
        gap: 20,
        backgroundColor: "white",
        borderRadius: 16,
        padding: "20px 32px",
        color: "black",
        fontFamily: "Helvetica, Arial, sans-serif",
        fontSize: 48,
        fontWeight: 600,
        ...style,
      }}
    >
      <div
        style={{
          width: 8,
          alignSelf: "stretch",
          borderRadius: 4,
          backgroundColor: accentColor,
        }}
      />
      {children}
    </Interactive.Div>
  );
};

const lowerThirdSchema = {
  ...Interactive.childrenSchema,
  accentColor: {
    type: "color",
    default: "#0b84f3",
    description: "Akzentfarbe",
  },
} as const satisfies InteractivitySchema;

export const LowerThird = Interactive.withSchema({
  Component: LowerThirdInner,
  componentName: "LowerThird",
  schema: lowerThirdSchema,
  wrapInSequence: true,
});
import { Composition } from "remotion";
import { LowerThird } from "./LowerThird";
import { MyScene } from "./MyScene";

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="MyScene"
        component={MyScene}
        durationInFrames={60}
        fps={30}
        width={1280}
        height={720}
      />
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={30}
        fps={30}
        width={1280}
        height={720}
        defaultProps={{
          children: "Jane Doe, Product Designer",
          accentColor: "#0b84f3",
        }}
      />
    </>
  );
};

Verzögerung und Zuschnitt

Setze Timing direkt auf Komponenten, die es unterstützen. Siehe Timing-Props für die unterstützten Props, Beispiele und die Reihenfolge der Operationen.

Karten

Siehe Remotion Maps, falls Karten im Video enthalten sein sollen.

Texthervorhebungen und Annotationen

Siehe text-highlights.md für Texthervorhebungen (Highlight-Marker), Kreise, Unterstreichungen, Durchstreichungen, durchgestrichenen Text, Boxen.

Videos mit mehreren Szenen

Siehe multi-scene-video.md, falls du ein Video mit mehreren aufeinanderfolgenden Szenen erstellen möchtest.

Verbundene Compositions

Wenn eine Szene oder eine Gruppe von Ebenen eine eigene editierbare Timeline verdient, folge connected-compositions.md. Bevorzuge diese Struktur für umfangreiche Szenen in einem Video mit mehreren Szenen.

Pre-compose-Aktion

Für eine Studio-Anfrage wie Pre-compose Ambient glow (src/BarChart.tsx:134) finde das ausgewählte Sequenz-Markup an der angegebenen Stelle. Erstelle eine verbundene Composition gemäß connected-compositions.md: extrahiere das Markup in eine benannte Komponente, mache sie vorzugsweise interaktiv mit Interactive.withSchema({wrapInSequence: true}) und registriere denselben exportierten Komponentenverweis mit einer eindeutigen <Composition> im Root. Rendere die interaktive Komponente direkt mit ihren Timing-Props oder als einziges Kind einer Sequenz, wenn dieser Wrapper einen Zweck erfüllt. Wenn der ausgewählte Knoten bereits eine Sequenz ist, behalte seine Props und extrahiere seine Kinder. Die Registrierung benötigt Abmessungen, fps, Dauer und defaultProps äquivalent zur übergeordneten Verwendung. Eine Komponentenextraktion ohne registrierte Composition erfüllt eine Pre-compose-Anfrage nicht.

Übernimm vererbte Styles wie fontFamily und Schriftart-Ladung in die extrahierte Komponente; siehe Elternunabhängigkeit.

Voiceover

Siehe voiceover.md zum Hinzufügen eines KI-generierten Voiceovers zu Remotion-Compositions mit ElevenLabs TTS.

Videos einbetten

Siehe embedding-videos.md für fortgeschrittenes Wissen zum Einbetten von Videos - Zuschnitt, Lautstärke, Geschwindigkeit, Schleifen, Tonhöhe.

Audio einbetten

Siehe audio.md für fortgeschrittene Audio-Funktionen wie Zuschnitt, Lautstärke, Geschwindigkeit, Tonhöhe.

Zuschneiden

Siehe cropping.md, falls das sichtbare Rechteck einer Komponente beschnitten werden muss.

Übergänge

Siehe transitions.md für Szenenübergangsmuster.

Bewegungsunschärfe

Wenn du Bewegungsunschärfe oder eine Bewegungsspur hinzufügen möchtest, lies motion-blur.md für den bevorzugten HTML-in-Canvas-Ansatz, Vorschauanforderungen und Alternativen.

Visuelle und Pixel-Effekte

Wenn du einen visuellen Effekt hinzufügen möchtest, identifiziere zuerst das Element oder die Gruppe, die er betreffen soll. Wenn die Zielkomponente effects unterstützt und ein unten aufgeführtes, integriertes Effekt dem Wunsch entspricht, bevorzuge diesen Effekt gegenüber einer CSS-Näherung. Wende z. B. vignette() direkt auf das effects-Prop eines <Video> an, wenn die Vignette nur dieses Video betreffen soll.

Diese Komponenten akzeptieren effects direkt:

Import vonKomponenten
remotion<Img>, <CanvasImage>, <AnimatedImage>, <Solid>, <HtmlInCanvas>
@remotion/media<Video>
@remotion/gif<Gif>
@remotion/rive<RemotionRiveCanvas>
@remotion/shapes<Arrow>, <Callout>, <Circle>, <Ellipse>, <Heart>, <Pie>, <Polygon>, <Rect>, <Spark>, <Star>, <Triangle>

Gewöhnliche HTML-Elemente, <Interactive.*>-DOM-Wrapper, <AbsoluteFill> und <Sequence> akzeptieren effects nicht direkt. Um Effekte auf ihren Inhalt anzuwenden, wickele sie in <HtmlInCanvas> ein und setze das effects-Prop auf den Wrapper.

Für andere Elemente bevorzuge reguläres HTML und CSS, wo geeignet. Ziehe in Betracht, eine Gruppe in <HtmlInCanvas> einzuwickeln, wenn eine Nachbearbeitung der gesamten Gruppe angemessen ist und die Umgebung dies unterstützt.

Lies effects.md, bevor du einen Effekt anwendest, für Importe, Parameter und Einrichtung. Es behandelt auch wiederverwendbare benutzerdefinierte Effekte mit createEffect(), wenn kein integrierter Effekt passt.

Verfügbare Effekte

Integrierte Effekte aus @remotion/effects:

  • Farbe und Ton: brightness(), colorCorrection(), colorKey(), contrast(), duotone(), exposure(), grayscale(), hue(), invert(), levels(), linearGradient(), linearGradientTint(), lut(), saturation(), shadowsHighlights(), thermalVision(), tint(), vibrance(), whiteBalance().
  • Unschärfe und Spuren: blur(), lightTrail(), linearProgressiveBlur(), radialProgressiveBlur(), regionBlur(), trail().
  • Verzerrung und Überlagerung: bend(), chromaticAberration(), codeRain(), colorMatrix(), colorReplace(), convolution(), crt(), displacementMap(), distanceBlur(), dropShadow(), dust(), edgeWork(), emboss(), filmGrain(), fisheye(), flash(), gaussianBlur(), glitch(), glow(), grainMerge(), hueRotate(), innerShadow(), kaleidoscope(), lens(), lightLeaks(), mirror(), noise(), oldFilm(), outline(), overlay(), pageCurl(), perspective(), pinch(), pixelate(), polaroid(), posterize(), pulse(), radialBlur(), ripple(), rotate3d(), saturate(), sepia(), sharpen(), sketch(), slitscan(), smoke(), solarize(), speedLines(), splash(), strobe(), swirl(), textureOverlay(), threshold(), tiltShift(), timeBlur(), tornEdges(), twin(), vignette(), water(), wave(), wind().

Weitere Details zu Parametern und Standardwerten findest du in effects.md.

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

Verwandte Skills