Produkt-Demo-Video
Verwandeln Sie statische Produkt-Screenshots oder Designs in eine polierte, animierte Demo – kein Bildschirmrekorder, kein zitternder echter Cursor, keine UI-Versionsabweichung. Rahmens Sie jeden Bildschirm ein, lassen Sie einen synthetischen Cursor zum exakten Pixel gleiten, erzeugen Sie die Klickwelle, zoomen Sie dann auf den Teil, der zählt, und beschriften Sie die Funktion. Da die Eingabe statisch und die Bewegung Code ist, ist jeder Wert deterministisch und wird identisch neu gerendert, wenn sich der Screenshot ändert.
Wann verwenden
- App-/SaaS-Demo, Funktionsankündigung, Onboarding-Walkthrough (15–90s).
- Anzeigen eines Klickpfades durch eine UI, von der Sie nur Screenshots/Designs haben.
- Hervorheben eines Fokusbereichs eines dichten Dashboards (Zoom-zu-Region + Callout).
Warum Screenshots Bildschirmaufzeichnungen schlagen
| Bildschirmaufzeichnung | Screenshot-getrieben (diese Fähigkeit) |
|---|---|
| Echter Cursor driftet, überschießt, zittert | Cursor folgt einem sanften Pfad zu einem exakten Ziel |
| Ganzen Take neu aufnehmen, um einen Schritt zu korrigieren | Ein PNG austauschen, denselben Code neu rendern |
| UI ändert sich während des Takes, Maus sucht | Jeder Bildschirm ist ein sauberes Asset im Endzustand |
| Schwer zu zoomen ohne Pixelbildung | Screenshots mit 2× exportieren (DPR 2) → scharfe Zooms |
Erfassen Sie Eingaben in doppelter Display-Größe (Retina / deviceScaleFactor: 2), damit ein 2×-Zoom scharf bleibt. Siehe references/screenshot-demo.md für ein Playwright-Erfassungsskript.
Der Demo-Loop
Jede Demo folgt dem gleichen Takt, wiederholt pro Funktion. Halten Sie lange genug zum Lesen, bewegen Sie sich mit Absicht, führen Sie den Klick aus, zeigen Sie das Ergebnis.
| Takt | Budget | Aufgabe |
|---|---|---|
| Auf Bildschirm einstellen | 0,5–1s | Dem Zuschauer Orientierung geben, bevor sich etwas bewegt |
| Cursor zum Ziel bewegen | 0,4–0,8s | Sanfte Bewegung (Ease-In-Out), nicht linear |
| Klickwelle + Zustandsänderung | 0,2–0,3s | Taktiles Feedback; der Bildschirm reagiert im selben Frame |
| Zoom auf Fokusregion | 1–2s hinein, 2–4s halten | 2× ist der Sweet Spot – genug Detail, behält Kontext |
| Funktion beschriften | Halten mit dem Zoom | Ein Nutzen-Satz, z. B. „Export mit einem Klick“ |
Bewegen Sie die Kamera oder den Cursor, selten beides gleichzeitig – zwei gleichzeitige Bewegungen teilen die Aufmerksamkeit.
Screenshot einrahmen
Ein roher Screenshot wirkt wie ein Bug-Report; ein gerahmter wirkt wie ein Produkt. Umgeben Sie jeden Bildschirm mit einem Browser- oder Geräte-Chrome auf einem markierten Hintergrund.
// Browserrahmen – einen Screenshot hineinwerfen, ein glaubwürdiges Fenster erhalten
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>
);
Setzen Sie eine echte URL in die Adressleiste (Authentizität), fügen Sie einen weichen Schatten und einen Verlaufshintergrund hinzu. Für Mobilgeräte wechseln Sie zu einem Telefonrahmen mit Notch und abgerundeten Ecken. Behalten Sie den Rahmenstil in einem Theme-Objekt, damit jeder Bildschirm zusammenpasst.
Animierter Cursor + Klickwelle (die Kernbewegung)
Treiben Sie die Position aus dem aktuellen Frame, niemals aus einem CSS-Übergang oder einer Wanduhr – ein Videorenderer benötigt jeden Frame als reine Funktion der Framenummer, sonst desynchronisiert der Cursor und flackert.
import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";
// Sanfte Bewegung von `from` nach `to` zwischen startFrame und 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);
// Welle expandiert einmal, im Klick-Frame
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>
</>
);
};
Der Cursor verwendet Smoothstep, damit er aus dem Ruhezustand beschleunigt und in das Ziel hinein verlangsamt (ein linearer Cursor wirkt roboterhaft). Lösen Sie die Welle und die Zustandsänderung des Bildschirms im selben clickAt-Frame aus, sodass Ursache und Wirkung übereinstimmen. Siehe references/screenshot-demo.md für die vollständige Komposition, die mehrere Klicks über mehrere Bildschirme verdrahtet.
Zoomen zu einer Region (Ken Burns für UI)
Um auf ein fokussiertes Element zu zoomen, skalieren Sie den Bildschirm und setzen Sie transform-origin auf die Mitte dieses Elements, sodass der Zoom darauf zuläuft, anstatt auf die Bildschirmmitte.
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>
);
};
Begrenzen Sie den Zoom auf etwa 2× (über 3× verliert den Kontext und desorientiert). Halten Sie den gezoomten Zustand 2–4s – lang genug, um das Detail zu lesen – und zoomen Sie dann sanft zurück, bevor Sie zum nächsten Bildschirm übergehen. Kombinieren Sie den Zoom immer mit einem Callout, niemals roh.
Callout-Anmerkungen und Feature-Bildunterschriften
Kommentieren Sie den Wendepunkt und lassen Sie ihn dann verschwinden. Ein Callout ist ein Label + ein Verbinder (Pfeil, Kreis oder Unterstreichung), der auf das fokussierte Element zeigt.
- Heben Sie das Element mit einem weichen Ring oder einem abgedunkelten Hintergrund hervor (
box-shadow: 0 0 0 9999px rgba(0,0,0,.45)schneidet ein Spotlight-Loch). - Beschriften Sie einen Nutzen pro Takt – kurzer Nominalausdruck („Echtzeit-Analyse“), kein Satz. Erscheint beim Zoom, verschwindet vor dem nächsten Schritt.
- Halten Sie Bildunterschriften im unteren Drittel und innerhalb des titelsicheren Bereichs, damit sie Beschnitte auf 9:16 überleben.
Siehe references/annotation-and-transitions.md für Spotlight-Masken, Pfeilverbinder und eine getippte Bildunterschriftenspur.
Bild-zu-Bild-Übergänge
Zwischen den Schritten bewegen Sie Bildschirme mit einer konsistenten Sprache. Wählen Sie eine und bleiben Sie dabei:
| Übergang | Gefühl | Verwendung |
|---|---|---|
| Schieben / Gleiten | „Nächster Schritt“-Vorwärtsbewegung | Lineare Walkthroughs |
| Cross-Zoom | Schwung, app-ähnlich | Springen in eine Detailansicht |
| Crossfade | Ruhig, neutral | Kontextwechsel (Einstellungen → Dashboard) |
| Match-Cut auf einem gemeinsamen Element | Nahtlos | Gleiche Karte bleibt über Bildschirme hinweg bestehen |
Halten Sie den ausgehenden Bildschirm, bis sein Klick abgeschlossen ist, und führen Sie dann den Übergang durch – niemals während der Bewegung schneiden. Siehe references/annotation-and-transitions.md.
Ausgabe-Checkliste
- Eingaben mit 2× (DPR 2) erfasst; Zooms bleiben bei 2× scharf.
- Jeder Cursor-/Zoomwert ist eine reine Funktion von
useCurrentFrame()– keine CSS-Übergänge für animierte Zustände. - Der Cursor bewegt sich auf einem sanften Pfad (Smoothstep); Welle und Zustandsänderung werden im selben Klick-Frame ausgelöst.
- Jeder Bildschirm eingerahmt (Browser/Gerät) mit einer echten URL und einem konsistenten Theme.
- Eine Bewegung gleichzeitig (Kamera oder Cursor); Zoom ≤ 2×, 2–4s gehalten.
- Ein Nutzen-Caption pro Takt, im titelsicheren unteren Drittel; Übergänge verwenden eine konsistente Sprache.
Liefern und verifizieren (gerenderte Standbilder → MP4)
Verpackter Helfer (
scripts/): Kacheln Sie Ihre Standbilder mitscripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png, bestätigen Sie dann die Kodierung mitscripts/probe-mp4.sh out.mp4 [WxH] [fps]. Siehescripts/README.md.
Dies ist Heavy-Tier: Das Lieferobjekt ist ein MP4, keine Live-Seite. Die Demo nimmt Benutzer-Assets auf – Screenshots/Designs, optionales Logo – daher besteht die Überprüfung hauptsächlich darin: Wurde das richtige Bild tatsächlich geladen und im Rahmen platziert?
Ausgabevertrag:
- Ein Remotion-Projekt mit der registrierten Komposition (
<Composition>+ Zod-schema+defaultProps); jeder Cursor-/Zoom-/Übergangswert eine reine Funktion vonuseCurrentFrame()(keine CSS-Übergänge, Timer,Date.now()). - Screenshots geladen über
staticFile(), abgesichert mitdelayRender/continueRender, sodass das 2×-PNG (und jede Web-Schriftart) vorhanden ist, bevor der Frame gerendert wird – sonst erscheint der Bildschirm leer mitten in der Demo. - Lieferobjekt = das gerenderte
out/*.mp4plus das Projekt (erneut rendern, wenn der Screenshot geändert wird).
Überprüfungsschleife – Standbilder rendern → prüfen → kodieren. Zuerst günstige PNGs, Video erst, wenn sie korrekt sind.
# Framegenaue Standbilder MIT DEN PROPS, DIE SIE AUSLIEFERN (echte Screenshot-Pfade), nicht defaultProps
npx remotion still ProductDemo out/f-settle.png --frame=15 --props=demo.json # Bildschirm eingerahmt, vor Bewegung
npx remotion still ProductDemo out/f-click.png --frame=90 --props=demo.json # Cursor auf Ziel + Welle
npx remotion still ProductDemo out/f-zoom.png --frame=150 --props=demo.json # gezoomt auf Fokusregion + Bildunterschrift
# End-Frame = durationInFrames - 1 (npx remotion compositions liest es)
Überprüfen Sie jedes PNG auf Treue (korrekter Screenshot geladen; Cursor auf dem exakten Pixel; Klickwelle und Zustandsänderung im selben Frame; Bildunterschrift richtig) UND auf Artefakte (Bild leer/nicht geladen, Bildschirm außerhalb der Leinwand, Zoom verpixelt oder über den Rahmen hinaus, Bildunterschrift außerhalb des titelsicheren unteren Drittels, falsches Seitenverhältnis/Letterboxing).
# Erst, wenn Standbilder sauber sind:
npx remotion render ProductDemo out/demo.mp4 --props=demo.json
npx remotion render ProductDemo out/demo.gif --props=demo.json --codec=gif # README erster Bildschirm-Beweis
Stapelverarbeitung (eine Vorlage, viele Produkte): Wenn Sie die Demo pro Produkt neu einkleiden, überprüfen Sie ZUERST die Props eines repräsentativen Produkts über Standbilder, bevor Sie den Katalog stapelweise rendern – fangen Sie einen Leerbild- oder Off-Canvas-Fehler einmal, nicht N-mal.
Bevor Sie fertig sind:
- Standbilder render sauber bei Einrichtung / Klick / Ende – keine Fehler, Screenshots tatsächlich geladen (nicht leer).
- Cursor landet auf dem exakten Ziel; Welle + Zustandsänderung im selben Frame; Bildunterschrift korrekt und im titelsicheren unteren Drittel.
- Alle Bewegungen frame-getrieben – keine CSS-Übergänge / Timer /
Date.now()/Math.random(). - Die ausgelieferten Props (echte Screenshot-Pfade) render korrekt, nicht nur
defaultProps. - Vollständiges MP4 kodiert und abspielbar; (optional) GIF für die README gerendert.
Referenzdateien
references/screenshot-demo.md– ein vollständiges, ausführbares Remotion-Demo: Erfassen von Screenshots mit 2× mit Playwright, die Browser-/Telefonrahmen-Komponenten, eine mehrschrittige Komposition, die einen Cursor mit Klickwellen über mehrere Bildschirme führt, und ein Render-Befehl.references/annotation-and-transitions.md– Spotlight-Masken, Pfeil-/Kreis-Callout-Verbinder, eine getippte Feature-Bildunterschriftenspur und die vier Bild-zu-Bild-Übergänge mit frame-getriebenen Implementierungen und Mehrfach-Seitenverhältnis- (16:9 / 9:16) Safe-Area-Notizen.