youtube/remotion/src/folge02/szene7.tsx
Pascal Scholz cbacc2fd97 Remotion: Baukasten für alle Folgen, Szenen einzeln renderbar, Stimmen-Skript
- gemeinsame Bausteine nach src/baukasten/, Bibliothek nach public/bibliothek/, Orte nach public/orte/
- je Szene eine Komposition (F02-01-…), werkzeuge/render.mjs rendert nur geänderte Szenen
  und klebt sie zusammen; --entwurf mit halber Auflösung und eingeblendeter Zeit/Zeile
- werkzeuge/stimmen.mts erzeugt ElevenLabs-Aufnahmen, Zeitplan übernimmt ihre Länge
- Folge 2: Geh- und Hinsetz-Clips statt Standbild-Wechsel, neue Vögel, fallendes Blatt,
  Noa malt den Kalender (Blick von oben)
- ANLEITUNG_PC.md für Studio und Rendern unter Windows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:43:56 +00:00

124 lines
4.3 KiB
TypeScript

import React from 'react';
import {useCurrentFrame} from 'remotion';
import {ClipFigur, Figur, Kamera} from '../baukasten/figur';
import {Blende, Vignette, atmen, kamFahrt} from '../baukasten/helfer';
import {Nest, Schnee, Zzz} from '../baukasten/natur';
import {BODEN, NEST_X, Ort} from '../baukasten/orte';
import {Untertitel} from '../baukasten/untertitel';
import {plane} from './plan';
// Szene 7: Gute Nacht, Pino. Nacht im Garten, die erste Schneeflocke fällt.
const NOA_X7 = 1140;
const LULU_X7 = 1800;
const LATERNE = {x: 1290, y: BODEN + 10};
export const S7 = plane('s7',
[
{id: 'bild0', dauer: 3.4},
{id: 'n1', wer: 'NOA', text: 'Schlaf gut, Pino. Nur bis morgen.', leise: true},
{id: 'e1', wer: 'ERZ', text: 'Ab dem 1. Dezember öffnen Noa, Lulu und Pino jeden Tag ein Türchen.'},
{id: 'e2', wer: 'ERZ', text: 'Und du kannst mitmachen! Schaffst du es mit Pino bis zum Winterfest?', pause: 3},
{id: 't1', wer: 'NOA_LULU', text: 'Tschüss! Bis bald!', leise: true, dauer: 2.6},
{id: 'p1', wer: 'PINO', text: '… bis bald …', leise: true, dauer: 2.6},
],
{vorlauf: 0.5, nachlauf: 1.6},
);
const Laterne: React.FC<{frame: number}> = ({frame}) => {
const flackern = 0.92 + 0.08 * Math.sin(frame * 0.5) * Math.sin(frame * 0.17);
return (
<>
<div style={{position: 'absolute', left: LATERNE.x - 18, top: LATERNE.y - 92, width: 36, height: 8, borderRadius: 3, background: '#3a3036'}} />
<div style={{position: 'absolute', left: LATERNE.x - 3, top: LATERNE.y - 112, width: 6, height: 22, background: '#3a3036'}} />
<div
style={{
position: 'absolute',
left: LATERNE.x - 24,
top: LATERNE.y - 84,
width: 48,
height: 70,
borderRadius: 10,
background: `radial-gradient(circle at 50% 55%, #fff4c2 0%, #ffc95e ${40 * flackern}%, #e08a3c 100%)`,
border: '5px solid #3a3036',
}}
/>
<div style={{position: 'absolute', left: LATERNE.x - 30, top: LATERNE.y - 16, width: 60, height: 12, borderRadius: 4, background: '#3a3036'}} />
</>
);
};
export const Szene7: React.FC = () => {
const f = useCurrentFrame();
const {z, dauer} = S7;
const flackern = 0.92 + 0.08 * Math.sin(f * 0.5) * Math.sin(f * 0.17);
const winken = z.t1.start - 6;
const kam = kamFahrt(f, [
[0, 1300, 600, 1.15],
[z.bild0.ende, 1350, 700, 1.4],
[z.n1.ende, NEST_X, 820, 2.0],
[z.e2.start, 1380, 700, 1.45],
[z.t1.start, 1400, 680, 1.4],
[z.p1.start, NEST_X, 850, 2.6],
[dauer, NEST_X, 860, 2.9],
]);
return (
<>
<Kamera {...kam}>
<Ort name="garten_nacht" />
<div style={{position: 'absolute', inset: 0, filter: 'brightness(0.62) saturate(0.8) hue-rotate(-8deg)'}}>
{f < winken ? (
<Figur
pose={[
[0, 'noa_A6_blick-rechts'],
[z.bild0.ende - 20, 'noa_A2_augen-halb'],
[z.e1.start, 'noa_A3_lachen'],
]}
x={NOA_X7}
y={BODEN}
stauchY={atmen(f, 2)}
/>
) : (
<ClipFigur name="noa_C4_winken" start={winken} tempo={1.2} x={NOA_X7} y={BODEN} />
)}
<Nest x={NEST_X} y={BODEN} seite="hinten" />
<div style={{position: 'absolute', inset: 0, clipPath: `inset(0 0 ${1080 - BODEN - 4}px 0)`}}>
<Figur pose="pino_B_schlaeft-eingerollt" x={NEST_X + 10} y={BODEN - 34} stauchY={atmen(f, 1, 0.02)} schatten={false} />
</div>
<Nest x={NEST_X} y={BODEN} seite="vorne" />
<Figur
pose={[
[0, 'lulu_A5_blick-links'],
[z.bild0.ende, 'lulu_A2_augen-halb'],
[z.t1.start, 'lulu_A3_lachen'],
]}
x={LULU_X7}
y={BODEN - 10}
stauchY={atmen(f, 3, 0.006)}
drehung={f > z.t1.start && f < z.t1.ende ? Math.sin((f - z.t1.start) * 0.25) * 2 : 0}
/>
</div>
{/* warmes Licht der Laterne */}
<div
style={{
position: 'absolute',
left: LATERNE.x - 520,
top: LATERNE.y - 560,
width: 1040,
height: 900,
background: `radial-gradient(closest-side, rgba(255, 200, 110, ${0.42 * flackern}), rgba(255, 180, 90, 0.12) 55%, rgba(255, 180, 90, 0) 100%)`,
mixBlendMode: 'screen',
}}
/>
<Laterne frame={f} />
<Zzz x={NEST_X + 50} y={BODEN - 110} start={20} s={0.7} />
<Schnee start={30} ziel={[NEST_X - 20, BODEN - 70]} />
</Kamera>
<Vignette staerke={0.5} />
<Untertitel zeilen={S7.liste} />
<Blende dauer={dauer} rein={16} raus={0} farbe="#1c1a24" />
</>
);
};