Communitygithub.com

Guilhermeslvg/video-portfolio

Bonnes pratiques pour le contenu, l'animation et les effets vidéo avec Remotion

Qu'est-ce que video-portfolio ?

Ce skill est un guide complet de bonnes pratiques pour écrire du balisage React pour Remotion, centré sur la création de vidéos portfolio professionnelles. Il couvre les règles fondamentales pour préserver les modifications utilisateur, utiliser `useCurrentFrame()` et `interpolate()` pour les animations (en évitant les transitions CSS ou les classes Tailwind), et structurer le balisage selon les meilleures pratiques d'interactivité de Remotion. Le guide détaille l'utilisation des composants médias (`<Video>`, `<Audio>`, `<CanvasImage>`, `<AnimatedImage>`), la gestion des ressources via `staticFile()`, et l'application de timing avec `premountFor` pour assurer des transitions fluides. Il inclut également des sections spécialisées sur les scènes multi-séquences, les compositions connectées, les transitions, le flou de mouvement, les effets visuels (comme le vignettage ou les corrections de couleur), les superpositions de texte, les cartes, les voix off AI, et l'incrustation de vidéos/audio. Ce skill est utile pour tout développeur utilisant Remotion (supporté par Claude Code, Cursor, et d'autres agents IA) souhaitant créer des vidéos dynamiques et éditables dans Studio.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Aperçu

Depuis le README du skill

Documentation

Ceci est un guide pour l'écriture de balisage React Remotion. Si cela n'est pas pertinent, chargez plutôt les Bonnes Pratiques Remotion.

Préserver les modifications utilisateur

Les utilisateurs peuvent effectuer des modifications dans le code en dehors de la conversation.

Si vous détectez un changement surprenant effectué entre-temps, ne l'écrasez pas, supposez qu'il était intentionnel ou demandez une confirmation.

Règles générales

À l'intérieur d'un composant de composition ou de scène, récupérez fps avec useVideoConfig() pour tous les calculs secondes-à-images, y compris les propriétés de timing comme from, trimBefore, durationInFrames et premountFor. Garder l'expression sur le nœud JSX, comme trimBefore={4 * fps}, permet à Studio de modifier le timing et le maintient correct si la fréquence d'images de la composition change.

Animez les animations avec useCurrentFrame() et interpolate(). Les transition ou animation CSS ne s'afficheront pas correctement, elles doivent être refactorisées. Les classes d'animation Tailwind ne s'afficheront pas correctement, elles doivent être refactorisées.

Utilisez Easing.bezier() et Easing.spring() pour personnaliser le timing.

Structurez votre balisage selon les Bonnes Pratiques d'Interactivité Remotion. Préférez Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) pour les scènes avec des propriétés éditables. Leur balisage interne peut retourner un fragment ; le wrapper gère la disposition et les styles d'instance. Les éléments dimensionnés comme les tiers inférieurs peuvent conserver la disposition par défaut et transmettre style à leur propre racine. Enregistrez les scènes réutilisables comme des compositions connectées. Placez le timing directement sur les composants qui le supportent ; évitez les wrappers <Sequence> redondants. Donnez à chaque composant temporisé qui supporte premountFor une seconde de pré-montage : premountFor={fps}, où fps provient de useVideoConfig(). Appliquez cela aux médias, composants interactifs, <Sequence>, <TransitionSeries.Sequence>, et <TransitionSeries.Overlay>, y compris les composants temporisés imbriqués dans les scènes. Pré-montez également l'élément de la timeline parent lorsqu'un élément imbriqué doit se monter avant que le parent ne commence. Un composant sans premountFor, comme <TransitionSeries.Transition>, n'a pas besoin de substitut.

Studio modifie le nœud source JSX qui a créé un élément. Créez chaque enregistrement de composition, clip, scène, couche et séquence qui doit être modifiable indépendamment comme son propre nœud JSX, avec ses propriétés éditables en ligne. Les boucles programmatiques sont appropriées lorsque les instances générées sont intentionnellement contrôlées comme un modèle source unique, pas lorsque les utilisateurs doivent modifier les instances séparément.

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

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

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

Gardez l'appel interpolate() en ligne dans la propriété style. Utilisez les propriétés CSS scale, translate, rotate plutôt que transform.

// 👍 Images clés éditables en ligne et raccourcis de transformation
style={{
  scale: interpolate(frame, [0, 100], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
    output: 'perceptual-scale' // Pour les animations `scale`, utilisez "output: 'perceptual-scale'"
  }),
  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}),
  }),
}}

// 👎 Les valeurs non en ligne et les chaînes de transformation deviennent plus difficiles à modifier dans Studio
const scale = interpolate(frame, [0, 100], [0, 1]);

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

Ressources

Placez les ressources dans le dossier public/ à la racine de votre projet. Utilisez staticFile() pour référencer les fichiers du dossier public/.

Composants média

Ajoutez la vidéo et l'audio avec <Video> et <Audio> depuis @remotion/media. Ajoutez les images avec le composant <CanvasImage>. Ajoutez les GIFs animés, APNG, WebP ou AVIF avec <AnimatedImage>, utilisez @remotion/gif si vous n'utilisez pas Chrome. Utilisez staticFile() pour les fichiers dans public/ ou passez une URL distante directement :

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} />
    </>
  );
};

Si la composition est principalement une timeline de clips vidéo ou audio, lisez video-editing.md avant de choisir sa structure source.

Exemple de scène

Une vidéo de fond avec un tiers inférieur. Le tiers inférieur est un composant interactif avec sa propre timeline, enregistré comme une composition connectée. Son texte est passé en tant que children et accentColor est une propriété modifiable. La transition en fondu est animée en ligne au site d'appel.

// 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="Fond"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{
          position: "absolute",
          width: "100%",
          height: "100%",
        }}
      />
      <LowerThird
        name="Tiers inférieur"
        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, Designer de produits
      </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: "Couleur d'accentuation",
  },
} 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, Designer de produits",
          accentColor: "#0b84f3",
        }}
      />
    </>
  );
};

Retarder, couper

Placez le timing directement sur les composants qui le supportent. Voir Propriétés de timing pour les propriétés prises en charge, les exemples et l'ordre des opérations.

Cartes

Voir Remotion Maps si vous souhaitez inclure des cartes dans la vidéo.

Surbrillances de texte et annotations

Voir text-highlights.md pour les surbrillances de texte (marqueurs de surbrillance), les cercles, les soulignements, les barrés, le texte rayé, les boîtes.

Vidéos multi-scènes

Voir multi-scene-video.md si vous prévoyez de faire une vidéo avec plusieurs scènes consécutives.

Compositions connectées

Lorsqu'une scène ou un groupe de couches mérite sa propre timeline modifiable, suivez connected-compositions.md. Préférez cette structure pour les scènes importantes dans une vidéo multi-scènes.

Action de pré-composition

Pour une demande Studio telle que Pré-composer Lueur ambiante (src/BarChart.tsx:134), trouvez le balisage de la séquence sélectionnée à l'emplacement donné. Créez une composition connectée, en suivant connected-compositions.md : extrayez le balisage dans un composant nommé, de préférence rendez-le interactif avec Interactive.withSchema({wrapInSequence: true}), et enregistrez la même référence de composant exporté avec un <Composition> unique dans la racine. Rendez le composant interactif directement avec ses propriétés de timing, ou comme seul enfant d'une séquence lorsque ce wrapper a un but. Si le nœud sélectionné est déjà une séquence, conservez ses propriétés et extrayez ses enfants. L'enregistrement a besoin de dimensions, fps, durée et defaultProps équivalents à son utilisation parente. Une extraction de composant sans composition enregistrée ne complète pas une demande de pré-composition.

Transmettez les styles hérités comme fontFamily et le chargement de polices dans le composant extrait ; voir indépendance parentale.

Voix off

Voir voiceover.md pour ajouter une voix off générée par IA aux compositions Remotion en utilisant ElevenLabs TTS.

Incrustation de vidéos

Voir embedding-videos.md pour des connaissances avancées sur l'incrustation de vidéos - coupe, volume, vitesse, boucle, hauteur.

Incrustation d'audio

Voir audio.md pour des fonctionnalités audio avancées comme la coupe, le volume, la vitesse, la hauteur.

Recadrage

Voir cropping.md si vous devez recadrer le rectangle visible d'un composant.

Transitions

Voir transitions.md pour les modèles de transition de scène.

Flou de mouvement

Lors de l'ajout d'un flou de mouvement ou d'une traînée de mouvement, lisez motion-blur.md pour l'approche HTML-dans-canvas préférée, les exigences de prévisualisation et les alternatives.

Effets visuels et de pixels

Lors de l'ajout d'un effet visuel, identifiez d'abord l'élément ou le groupe qu'il doit affecter. Si le composant cible prend en charge effects et qu'un effet intégré ci-dessous correspond à la demande, préférez cet effet à une approximation CSS. Par exemple, appliquez vignette() directement à la propriété effects d'un <Video> lorsque le vignettage doit affecter cette vidéo.

Ces composants acceptent directement effects :

Importé depuisComposants
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>

Les éléments HTML ordinaires, les wrappers DOM <Interactive.*>, <AbsoluteFill> et <Sequence> n'acceptent pas directement effects. Pour appliquer des effets à leur contenu, encapsulez-les dans <HtmlInCanvas> et mettez la propriété effects sur le wrapper.

Pour les autres éléments, préférez le HTML et CSS réguliers lorsque c'est approprié. Envisagez d'encapsuler un groupe dans <HtmlInCanvas> lorsque le post-traitement de l'ensemble du groupe est approprié et que l'environnement le prend en charge.

Lisez effects.md avant d'appliquer un effet pour les imports, les paramètres et la configuration. Il couvre également les effets personnalisés réutilisables avec createEffect() lorsqu'aucun effet intégré ne correspond.

Effets disponibles

Effets intégrés depuis @remotion/effects :

  • Couleur et teinte : brightness(), colorCorrection(), colorKey(), contrast(), duotone(), exposure(), grayscale(), hue(), invert(), levels(), linearGradient(), linearGradientTint(), lut(), saturation(), shadowsHighlights(), thermalVision(), tint(), vibrance(), whiteBalance().
  • Flou et traînées : blur(), lightTrail(), linearProgressiveBlur(), radialProgressiveBlur(), regionBlur(), trail().
  • Distorsion et transformation : billowDistortion(), mirror(), perspective(), pinchDistortion(), pixelate(), polaroid(), rippleDistortion(), scatter(), tile(), twirlDistortion(), waveDistortion(), zoom(), dropShadow(), glow(), vignette().
  • Composition : chromaKey(), displacementMap(), lumaChromaKey(), overlay(), shiftChannels().
  • Génération : regularPolygon(), roundedRectangle(), stars(), sunburst().
  • Gradients : linearGradient(), linearGradientTint(), radialGradient().

Individual skills in this repo

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

Skills associés