mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2026-01-27 06:33:18 +00:00
🎬 Add PortalPresentation skeleton composition: implement main composition structure with 9 scene sequences for the 90-second cinematic Portal presentation video.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
224
videos/src/PortalPresentation.tsx
Normal file
224
videos/src/PortalPresentation.tsx
Normal file
@@ -0,0 +1,224 @@
|
||||
import { AbsoluteFill, Sequence, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { inconsolataFont } from "./fonts/inconsolata";
|
||||
|
||||
/**
|
||||
* PortalPresentation - Main composition for the Einundzwanzig Portal presentation video
|
||||
*
|
||||
* Scene Structure (90 seconds total @ 30fps = 2700 frames):
|
||||
* 1. Logo Reveal (6s) - Frames 0-180
|
||||
* 2. Portal Title (4s) - Frames 180-300
|
||||
* 3. Dashboard Overview (12s) - Frames 300-660
|
||||
* 4. Meine Meetups (12s) - Frames 660-1020
|
||||
* 5. Top Länder (12s) - Frames 1020-1380
|
||||
* 6. Top Meetups (10s) - Frames 1380-1680
|
||||
* 7. Activity Feed (10s) - Frames 1680-1980
|
||||
* 8. Call to Action (12s) - Frames 1980-2340
|
||||
* 9. Outro (12s) - Frames 2340-2700
|
||||
*/
|
||||
export const PortalPresentation: React.FC = () => {
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
// Scene durations in seconds
|
||||
const SCENE_DURATIONS = {
|
||||
logoReveal: 6,
|
||||
portalTitle: 4,
|
||||
dashboardOverview: 12,
|
||||
meineMeetups: 12,
|
||||
topLaender: 12,
|
||||
topMeetups: 10,
|
||||
activityFeed: 10,
|
||||
callToAction: 12,
|
||||
outro: 12,
|
||||
};
|
||||
|
||||
// Calculate frame positions for each scene
|
||||
const sceneFrames = {
|
||||
logoReveal: { from: 0, duration: SCENE_DURATIONS.logoReveal * fps },
|
||||
portalTitle: {
|
||||
from: SCENE_DURATIONS.logoReveal * fps,
|
||||
duration: SCENE_DURATIONS.portalTitle * fps,
|
||||
},
|
||||
dashboardOverview: {
|
||||
from: (SCENE_DURATIONS.logoReveal + SCENE_DURATIONS.portalTitle) * fps,
|
||||
duration: SCENE_DURATIONS.dashboardOverview * fps,
|
||||
},
|
||||
meineMeetups: {
|
||||
from:
|
||||
(SCENE_DURATIONS.logoReveal +
|
||||
SCENE_DURATIONS.portalTitle +
|
||||
SCENE_DURATIONS.dashboardOverview) *
|
||||
fps,
|
||||
duration: SCENE_DURATIONS.meineMeetups * fps,
|
||||
},
|
||||
topLaender: {
|
||||
from:
|
||||
(SCENE_DURATIONS.logoReveal +
|
||||
SCENE_DURATIONS.portalTitle +
|
||||
SCENE_DURATIONS.dashboardOverview +
|
||||
SCENE_DURATIONS.meineMeetups) *
|
||||
fps,
|
||||
duration: SCENE_DURATIONS.topLaender * fps,
|
||||
},
|
||||
topMeetups: {
|
||||
from:
|
||||
(SCENE_DURATIONS.logoReveal +
|
||||
SCENE_DURATIONS.portalTitle +
|
||||
SCENE_DURATIONS.dashboardOverview +
|
||||
SCENE_DURATIONS.meineMeetups +
|
||||
SCENE_DURATIONS.topLaender) *
|
||||
fps,
|
||||
duration: SCENE_DURATIONS.topMeetups * fps,
|
||||
},
|
||||
activityFeed: {
|
||||
from:
|
||||
(SCENE_DURATIONS.logoReveal +
|
||||
SCENE_DURATIONS.portalTitle +
|
||||
SCENE_DURATIONS.dashboardOverview +
|
||||
SCENE_DURATIONS.meineMeetups +
|
||||
SCENE_DURATIONS.topLaender +
|
||||
SCENE_DURATIONS.topMeetups) *
|
||||
fps,
|
||||
duration: SCENE_DURATIONS.activityFeed * fps,
|
||||
},
|
||||
callToAction: {
|
||||
from:
|
||||
(SCENE_DURATIONS.logoReveal +
|
||||
SCENE_DURATIONS.portalTitle +
|
||||
SCENE_DURATIONS.dashboardOverview +
|
||||
SCENE_DURATIONS.meineMeetups +
|
||||
SCENE_DURATIONS.topLaender +
|
||||
SCENE_DURATIONS.topMeetups +
|
||||
SCENE_DURATIONS.activityFeed) *
|
||||
fps,
|
||||
duration: SCENE_DURATIONS.callToAction * fps,
|
||||
},
|
||||
outro: {
|
||||
from:
|
||||
(SCENE_DURATIONS.logoReveal +
|
||||
SCENE_DURATIONS.portalTitle +
|
||||
SCENE_DURATIONS.dashboardOverview +
|
||||
SCENE_DURATIONS.meineMeetups +
|
||||
SCENE_DURATIONS.topLaender +
|
||||
SCENE_DURATIONS.topMeetups +
|
||||
SCENE_DURATIONS.activityFeed +
|
||||
SCENE_DURATIONS.callToAction) *
|
||||
fps,
|
||||
duration: SCENE_DURATIONS.outro * fps,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<AbsoluteFill
|
||||
className="bg-gradient-to-br from-zinc-900 to-zinc-800"
|
||||
style={{ fontFamily: inconsolataFont }}
|
||||
>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover opacity-20"
|
||||
/>
|
||||
|
||||
{/* Scene 1: Logo Reveal (6s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.logoReveal.from}
|
||||
durationInFrames={sceneFrames.logoReveal.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Logo Reveal" sceneNumber={1} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 2: Portal Title (4s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.portalTitle.from}
|
||||
durationInFrames={sceneFrames.portalTitle.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Portal Title" sceneNumber={2} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 3: Dashboard Overview (12s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.dashboardOverview.from}
|
||||
durationInFrames={sceneFrames.dashboardOverview.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Dashboard Overview" sceneNumber={3} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 4: Meine Meetups (12s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.meineMeetups.from}
|
||||
durationInFrames={sceneFrames.meineMeetups.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Meine Meetups" sceneNumber={4} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 5: Top Länder (12s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.topLaender.from}
|
||||
durationInFrames={sceneFrames.topLaender.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Top Länder" sceneNumber={5} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 6: Top Meetups (10s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.topMeetups.from}
|
||||
durationInFrames={sceneFrames.topMeetups.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Top Meetups" sceneNumber={6} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 7: Activity Feed (10s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.activityFeed.from}
|
||||
durationInFrames={sceneFrames.activityFeed.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Activity Feed" sceneNumber={7} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 8: Call to Action (12s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.callToAction.from}
|
||||
durationInFrames={sceneFrames.callToAction.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Call to Action" sceneNumber={8} />
|
||||
</Sequence>
|
||||
|
||||
{/* Scene 9: Outro (12s) */}
|
||||
<Sequence
|
||||
from={sceneFrames.outro.from}
|
||||
durationInFrames={sceneFrames.outro.duration}
|
||||
premountFor={fps}
|
||||
>
|
||||
<PlaceholderScene name="Outro" sceneNumber={9} />
|
||||
</Sequence>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Placeholder component for scenes that haven't been implemented yet.
|
||||
* Displays a centered scene name with visual indicators.
|
||||
*/
|
||||
const PlaceholderScene: React.FC<{ name: string; sceneNumber: number }> = ({
|
||||
name,
|
||||
sceneNumber,
|
||||
}) => {
|
||||
return (
|
||||
<AbsoluteFill className="flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="w-24 h-24 mx-auto mb-6 rounded-full bg-orange-500/20 border-2 border-orange-500/50 flex items-center justify-center">
|
||||
<span className="text-4xl font-bold text-orange-500">{sceneNumber}</span>
|
||||
</div>
|
||||
<h2 className="text-4xl font-bold text-white mb-2">{name}</h2>
|
||||
<p className="text-lg text-neutral-400">Scene placeholder</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { Composition } from "remotion";
|
||||
import { MyComposition } from "./Composition";
|
||||
import { Nip05Tutorial } from "./Nip05Tutorial";
|
||||
import { Nip05TutorialMobile } from "./Nip05TutorialMobile";
|
||||
import { PortalPresentation } from "./PortalPresentation";
|
||||
|
||||
export const RemotionRoot: React.FC = () => {
|
||||
return (
|
||||
@@ -31,6 +32,14 @@ export const RemotionRoot: React.FC = () => {
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
<Composition
|
||||
id="PortalPresentation"
|
||||
component={PortalPresentation}
|
||||
durationInFrames={90 * 30}
|
||||
fps={30}
|
||||
width={1920}
|
||||
height={1080}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user