Vidéo de Démonstration Produit
Transformez des captures d'écran ou maquettes statiques de produit en une démo animée et soignée — sans enregistreur d'écran, sans curseur réel tremblant, sans dérive de version UI. Encadrez chaque écran, faites glisser un curseur synthétique jusqu'au pixel exact, faites onduler le clic, puis zoomez sur la partie importante et légendez la fonctionnalité. Parce que l'entrée est statique et le mouvement est du code, chaque valeur est déterministe et se re-rend à l'identique lorsque la capture d'écran change.
Quand l'utiliser
- Démo d'application / SaaS, annonce de fonctionnalité, parcours d'intégration (15–90 s).
- Montrer un chemin de clics à travers une UI dont vous n'avez que des captures d'écran ou maquettes.
- Mettre en évidence une région focale d'un tableau de bord dense (zoom sur région + callout).
Pourquoi les captures d'écran surpassent les enregistrements d'écran
| Enregistrement d'écran | Piloté par capture d'écran (cette compétence) |
|---|---|
| Le vrai curseur dérive, dépasse, tremble | Le curseur suit un chemin fluide jusqu'à une cible exacte |
| Réenregistrer toute la prise pour corriger une étape | Échanger un PNG, re-rendre le même code |
| L'UI change en cours de prise, la souris cherche | Chaque écran est un actif propre et final |
| Difficile de zoomer sans pixellisation | Exporter les captures en 2× (DPR 2) → zooms nets |
Capturez les entrées à deux fois la taille d'affichage (retina / deviceScaleFactor: 2) pour qu'un zoom 2× reste net. Voir references/screenshot-demo.md pour un script de capture Playwright.
La boucle de démo
Chaque démo suit le même rythme, répété par fonctionnalité. Tenez assez longtemps pour lire, déplacez-vous avec intention, posez le clic, révélez le résultat.
| Temps | Durée | Rôle |
|---|---|---|
| Poser sur l'écran | 0,5–1 s | Laisser le spectateur s'orienter avant tout mouvement |
| Le curseur se déplace vers la cible | 0,4–0,8 s | Mouvement fluide (ease-in-out), pas linéaire |
| Ondulation du clic + changement d'état | 0,2–0,3 s | Retour tactile ; l'écran réagit sur la même image |
| Zoom sur la région focale | 1–2 s entrée, tenir 2–4 s | 2× est le point idéal — assez de détails, garde le contexte |
| Légender la fonctionnalité | tenir avec le zoom | Une phrase de bénéfice, ex. « Export en un clic » |
Bougez la caméra ou le curseur, rarement les deux à la fois — deux mouvements simultanés divisent l'attention.
Encadrer la capture d'écran
Une capture d'écran brute ressemble à un rapport de bug ; une capture encadrée ressemble à un produit. Enveloppez chaque écran dans un chrome de navigateur ou d'appareil sur un fond de marque.
// Cadre de navigateur — déposez une capture, obtenez une fenêtre crédible
const BrowserFrame = ({ url, children }) => (
<div style={{ borderRadius: 12, overflow: "hidden", background: "#1e1e22",
boxShadow: "0 40px 80px rgba(0,0,0,.35)" }}>
<div style={{ height: 40, display: "flex", alignItems: "center", gap: 8, padding: "0 14px" }}>
{["#ff5f57", "#febc2e", "#28c840"].map((c) => (
<span key={c} style={{ width: 12, height: 12, borderRadius: "50%", background: c }} />
))}
<div style={{ marginLeft: 12, flex: 1, height: 24, borderRadius: 6, background: "#2b2b30",
color: "#9aa", fontSize: 13, display: "flex", alignItems: "center", padding: "0 10px" }}>
{url}
</div>
</div>
<div style={{ background: "#fff" }}>{children}</div>
</div>
);
Mettez une URL réelle dans la barre d'adresse (authenticité), ajoutez une ombre douce et un arrière-plan en dégradé. Pour le mobile, passez à un cadre de téléphone avec une encoche et des coins arrondis. Gardez le style du cadre dans un seul objet thème pour que chaque écran corresponde.
Curseur animé + ondulation de clic (le mouvement principal)
Pilotez la position à partir de l'image actuelle, jamais à partir d'une transition CSS ou d'une horloge murale — un rendu vidéo a besoin que chaque image soit une fonction pure du numéro d'image, sinon le curseur se désynchronise et scintille.
import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";
// Trajet fluide de `from` à `to` entre startFrame et startFrame+dur
const useCursor = (from, to, startFrame, dur) => {
const frame = useCurrentFrame();
const t = interpolate(frame, [startFrame, startFrame + dur], [0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => x * x * (3 - 2 * x) }); // smoothstep
return { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t };
};
export const Cursor = ({ from, to, startFrame, dur, clickAt }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const { x, y } = useCursor(from, to, startFrame, dur);
// L'ondulation s'étend une fois, à l'image du clic
const since = frame - clickAt;
const showRipple = since >= 0 && since < 18;
const r = spring({ frame: since, fps, config: { damping: 18 }, durationInFrames: 18 });
return (
<>
{showRipple && (
<div style={{ position: "absolute", left: x, top: y, transform: "translate(-50%,-50%)",
width: 12 + r * 60, height: 12 + r * 60, borderRadius: "50%",
border: "2px solid rgba(80,140,255,.9)", opacity: 1 - r }} />
)}
<svg width="28" height="28" viewBox="0 0 28 28"
style={{ position: "absolute", left: x, top: y, filter: "drop-shadow(0 2px 3px rgba(0,0,0,.4))" }}>
<path d="M3 2 L3 21 L9 15 L13 24 L16 23 L12 14 L20 14 Z" fill="#fff" stroke="#222" strokeWidth="1.4" />
</svg>
</>
);
};
Le curseur utilise smoothstep pour accélérer depuis l'arrêt et décélérer jusqu'à la cible (un curseur linéaire semble robotique). Déclenchez l'ondulation et le changement d'état de l'écran sur la même image clickAt pour que la cause et l'effet correspondent. Voir references/screenshot-demo.md pour la composition complète gérant plusieurs clics sur plusieurs écrans.
Zoom sur une région (Ken Burns pour UI)
Pour zoomer sur un élément focal, mettez à l'échelle l'écran et définissez transform-origin au centre de cet élément, afin que le zoom converge vers lui plutôt que vers le centre du cadre.
const ZoomToRegion = ({ target, scale = 2, startFrame, dur, children }) => {
const frame = useCurrentFrame();
const s = interpolate(frame, [startFrame, startFrame + dur], [1, scale],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => 1 - Math.pow(1 - x, 3) }); // easeOutCubic
return (
<div style={{ transform: `scale(${s})`, transformOrigin: `${target.x}px ${target.y}px`,
willChange: "transform" }}>
{children}
</div>
);
};
Limitez le zoom à environ 2× (au-delà de 3× perd le contexte et désoriente). Maintenez l'état zoomé 2–4 s — assez longtemps pour lire le détail — puis revenez en arrière avant de passer à l'écran suivant. Associez le zoom à un callout, jamais brut.
Annotations callout et légendes de fonctionnalités
Annotez l'inflexion, puis laissez-la disparaître. Un callout est une étiquette + un connecteur (flèche, cercle ou soulignement) qui pointe vers l'élément focal.
- Mettez en évidence l'élément avec un anneau doux ou un fond assombri (
box-shadow: 0 0 0 9999px rgba(0,0,0,.45)découpe un trou de projecteur). - Légendez un bénéfice par temps — une courte phrase nominale (« Analytics en temps réel »), pas une phrase. Entrez au zoom, sortez avant l'étape suivante.
- Gardez les légendes dans le tiers inférieur et dans la zone de sécurité titre pour qu'elles survivent aux recadrages en 9:16.
Voir references/annotation-and-transitions.md pour les masques de projecteur, les connecteurs fléchés et une piste de légendes typées.
Transitions entre écrans
Entre les étapes, déplacez les écrans avec un langage cohérent. Choisissez-en un et tenez-vous-y :
| Transition | Sensation | Utilisation |
|---|---|---|
| Pousser / glisser | Mouvement avant "étape suivante" | Parcours linéaires |
| Zoom croisé | Élan, comme une app | Sauter dans une vue détaillée |
| Fondu enchaîné | Calme, neutre | Changer de contexte (paramètres → tableau de bord) |
| Coupe franche sur un élément partagé | Sans couture | Même carte persiste entre les écrans |
Maintenez l'écran sortant jusqu'à ce que son clic se résolve, puis transitionnez — ne coupez jamais en plein mouvement. Voir references/annotation-and-transitions.md.
Liste de vérification de sortie
- Entrées capturées en 2× (DPR 2) ; les zooms restent nets en 2×.
- Chaque valeur de curseur/zoom est une fonction pure de
useCurrentFrame()— pas de transitions CSS pour l'état animé. - Le curseur se déplace sur un chemin fluide (smoothstep) ; l'ondulation et le changement d'état se déclenchent sur la même image de clic.
- Chaque écran est encadré (navigateur/appareil) avec une URL réelle et un thème cohérent.
- Un mouvement à la fois (caméra ou curseur) ; zoom ≤ 2×, maintenu 2–4 s.
- Une légende de bénéfice par temps, dans le tiers inférieur zone de sécurité titre ; les transitions utilisent un langage cohérent.
Livrer et vérifier (images clés rendues → MP4)
Aide empaquetée (
scripts/) : assemblez vos images clés avecscripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png, puis vérifiez l'encodage avecscripts/probe-mp4.sh out.mp4 [LxH] [fps]. Voirscripts/README.md.
C'est un niveau lourd : le livrable est un MP4, pas une page live. La démo ingère les actifs de l'utilisateur — captures d'écran/maquettes, logo optionnel — donc la vérification porte surtout sur la bonne image s'est-elle chargée et se trouve-t-elle dans le cadre.
Contrat de sortie :
- Un projet Remotion avec la composition enregistrée (
<Composition>+ zodschema+defaultProps) ; chaque valeur de curseur/zoom/transition est une fonction pure deuseCurrentFrame()(pas de transitions CSS, minuteries,Date.now()). - Les captures d'écran chargées via
staticFile(), verrouillées avecdelayRender/continueRenderpour que le PNG 2× (et toute police web) soit présent avant que l'image ne soit rendue — sinon l'écran apparaît vide en pleine démo. - Livrable = le
out/*.mp4rendu plus le projet (re-rendre lorsque la capture d'écran change).
Boucle de vérification — rendu d'images clés → inspection → encodage. Des PNG bon marché d'abord, la vidéo seulement une fois qu'ils sont corrects.
# Images clés exactes AVEC LES PROPS QUE VOUS ALLEZ LIVRER (chemins réels des captures), pas defaultProps
npx remotion still ProductDemo out/f-settle.png --frame=15 --props=demo.json # écran cadré, avant mouvement
npx remotion still ProductDemo out/f-click.png --frame=90 --props=demo.json # curseur sur la cible + ondulation
npx remotion still ProductDemo out/f-zoom.png --frame=150 --props=demo.json # zoomé sur la région focale + légende
# image finale = durationInFrames - 1 (npx remotion compositions le lit)
Inspectez chaque PNG pour la fidélité (capture correcte chargée ; curseur sur le pixel exact ; ondulation de clic et changement d'état sur la même image ; texte de légende correct) ET les artefacts (image vide/non chargée, écran hors cadre, zoom pixélisé ou dépassant le cadre, légende hors du tiers inférieur zone de sécurité titre, mauvais rapport hauteur/largeur ou letterbox).
# Seulement après que les images clés sont propres :
npx remotion render ProductDemo out/demo.mp4 --props=demo.json
npx remotion render ProductDemo out/demo.gif --props=demo.json --codec=gif # preuve dans le README du premier écran
Traitement par lot (un modèle, plusieurs produits) : lorsque vous ré-adaptez la démo par produit, vérifiez les props d'UN produit représentatif via des images clés avant de rendre le catalogue en lot — attrapez un bug d'image vide ou hors cadre une fois, pas N fois.
Avant de terminer :
- Les images clés se rendent proprement au repos / clic / fin — pas d'erreurs, les captures sont bien chargées (pas vides).
- Le curseur atterrit sur la cible exacte ; ondulation + changement d'état sur la même image ; texte de légende correct et dans le tiers inférieur zone de sécurité titre.
- Tout mouvement est piloté par image — pas de transitions CSS / minuteries /
Date.now()/Math.random(). - Les props livrées (chemins réels des captures) s'affichent correctement, pas seulement
defaultProps. - MP4 complet encodé et lisible ; (optionnel) GIF rendu pour le README.
Fichiers de référence
references/screenshot-demo.md— une démo Remotion complète et exécutable : capture d'écrans en 2× avec Playwright, les composants de cadre navigateur/téléphone, une composition multi-étapes qui déplace un curseur sur plusieurs écrans avec ondulations de clic, et une commande de rendu.references/annotation-and-transitions.md— masques de projecteur, connecteurs de callout fléchés/circulaires, une piste de légendes de fonctionnalités typées, et les quatre transitions entre écrans avec implémentations pilotées par image et notes de zone de sécurité multi-aspect (16:9 / 9:16).