Gehen ohne rutschende Füße (baukasten/gang.ts, gemessen an den Geh-Clips), Folge 8 „Noas Laterne“ in Arbeit, Lied 1: Pino hüpft wieder nach oben
- gang.ts: Geschwindigkeit aus der Fußbewegung im Clip, Start/Ende mit geschlossenen Füßen, ganze Schritte; GangTest als Prüfstand (Stiefel steht 21 Bilder auf ±1 px). Lulus Geh-Clip ist kein Gehen – nicht verschieben. - Lied 1: huepfer() ist negativ und wurde abgezogen – Pino sank bei jeder Zahl in den Boden. - folge08: Texte DE/ES/FR, Szene 2–7, Abendgarten mit Hauswand, Laterne mit Sternen/Igel, Wand-Schatten Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
75b28a9cab
commit
c6ca31402d
20 changed files with 1088 additions and 1 deletions
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import {Composition} from 'remotion';
|
||||
import {HintergrundAbend, PapierStruktur, VordergrundAbend} from './szenen/Hintergruende';
|
||||
import {LaterneTest} from './szenen/LaterneTest';
|
||||
import {GANGTEST_DAUER, GangTest} from './szenen/GangTest';
|
||||
import {VerformTest} from './szenen/VerformTest';
|
||||
import {FolgenKompositionen} from './baukasten/folge';
|
||||
import {NEST_BILD} from './baukasten/natur';
|
||||
|
|
@ -9,6 +10,7 @@ import {NestStill, ORTE, OrtStill} from './baukasten/orte';
|
|||
import {F01} from './folge01/Folge01';
|
||||
import {F02} from './folge02/Folge02';
|
||||
import {F03} from './folge03/Folge03';
|
||||
import {F08} from './folge08/Folge08';
|
||||
import {Banner, BannerRahmen, Profilbild} from './kanal/KanalBilder';
|
||||
import {VorschauF01} from './kanal/Vorschaubild';
|
||||
import {ShortKompositionen} from './shorts/Short';
|
||||
|
|
@ -23,6 +25,7 @@ export const Root: React.FC = () => (
|
|||
<>
|
||||
<Composition id="Lied01" component={Lied01} durationInFrames={LIED01_DAUER} {...BILD} />
|
||||
<Composition id="Lied03-DE" component={Lied03} durationInFrames={LIED03_DAUER} {...BILD} />
|
||||
<Composition id="GangTest" component={GangTest} durationInFrames={GANGTEST_DAUER} {...BILD} />
|
||||
<Composition id="LaterneTest" component={LaterneTest} durationInFrames={420} {...BILD} />
|
||||
<Composition id="VerformTest" component={VerformTest} durationInFrames={240} {...BILD} />
|
||||
<Composition id="HintergrundAbend" component={HintergrundAbend} durationInFrames={1} {...BILD} />
|
||||
|
|
@ -31,6 +34,7 @@ export const Root: React.FC = () => (
|
|||
<FolgenKompositionen folge={F01} />
|
||||
<FolgenKompositionen folge={F02} />
|
||||
<FolgenKompositionen folge={F03} />
|
||||
<FolgenKompositionen folge={F08} />
|
||||
<ShortKompositionen szenen={SZENEN_F01} shorts={SHORTS_F01} />
|
||||
<Composition id="Kanal-Profilbild" component={Profilbild} durationInFrames={1} fps={30} width={800} height={800} />
|
||||
<Composition id="Vorschau-F01" component={VorschauF01} durationInFrames={1} {...BILD} />
|
||||
|
|
|
|||
65
remotion/src/baukasten/gang.ts
Normal file
65
remotion/src/baukasten/gang.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import {GROESSE, NOA_HOEHE} from './figur';
|
||||
import {CLIPS, FIGUREN} from './posen';
|
||||
|
||||
// Gehen ohne rutschende Füße.
|
||||
// In den Geh-Clips (*_C1_gehen-schleife) geht die Figur auf der Stelle; der aufgesetzte Fuß wandert
|
||||
// dabei gleichmäßig nach hinten. Bewegt sich die Figur im Bild genau so schnell nach vorn, bleibt der
|
||||
// Fuß am Boden stehen. Gemessen am 05.11.2026 an den Alpha-Kanten der Füße (Bodenkontakt je Clipbild):
|
||||
// Noa: 10,1 px pro Clipbild (Video 292 px breit), Füße zusammen bei Clipbild ≈ 10,5, alle 10,25 Bilder ein Schritt
|
||||
// Pino: 3,6 px pro Clipbild (Video 438 px breit), Füße zusammen bei Clipbild ≈ 15, alle 15 Bilder ein Schritt
|
||||
// Lulus Clip ist kein Gehen (drei Beine bleiben stehen) – Lulu nie mit dem Clip über den Boden schieben.
|
||||
// Ein Gang beginnt und endet mit geschlossenen Füßen; die Strecke wird auf ganze Schritte gerundet.
|
||||
|
||||
type Messung = {pxProClipbild: number; videoBreite: number; fuesseZusammen: number; schrittBilder: number};
|
||||
const MESSUNG: Record<'noa' | 'pino', Messung> = {
|
||||
noa: {pxProClipbild: 10.1, videoBreite: 292, fuesseZusammen: 10.5, schrittBilder: 10.25},
|
||||
pino: {pxProClipbild: 3.6, videoBreite: 438, fuesseZusammen: 15, schrittBilder: 15},
|
||||
};
|
||||
|
||||
const CLIP_FPS = 24;
|
||||
const FPS = 30;
|
||||
|
||||
// Bildschirm-Pixel (in Szenenkoordinaten) je Clipbild bei Normalgröße
|
||||
const pxProClipbild = (figur: 'noa' | 'pino', hoehe = NOA_HOEHE * GROESSE[figur]) => {
|
||||
const m = MESSUNG[figur];
|
||||
const c = CLIPS[`${figur}_C1_gehen-schleife`];
|
||||
return ((m.pxProClipbild * c.b) / m.videoBreite) * (hoehe / FIGUREN[figur].refHoehe);
|
||||
};
|
||||
|
||||
export type Gang = {
|
||||
figur: 'noa' | 'pino';
|
||||
start: number; // Frame, an dem die Figur losgeht
|
||||
ende: number; // Frame, an dem sie wieder steht
|
||||
von: number;
|
||||
nach: number; // tatsächliches Ziel (auf ganze Schritte gerundet)
|
||||
tempo: number; // für ClipFigur
|
||||
clipStart: number; // für ClipFigur: so steht der Clip bei `start` auf „Füße zusammen“
|
||||
spiegeln: boolean; // Clips schauen nach links; nach rechts gehen = spiegeln
|
||||
x: (f: number) => number;
|
||||
geht: (f: number) => boolean;
|
||||
};
|
||||
|
||||
// Plant einen Gang von `von` nach (ungefähr) `nach`, Start bei Frame `start`.
|
||||
// tempo: wie schnell der Clip läuft (1,25 = jedes Videobild ein Clipbild); die Geschwindigkeit folgt daraus.
|
||||
export const gang = (figur: 'noa' | 'pino', start: number, von: number, nach: number, tempo = 1.25, hoehe?: number): Gang => {
|
||||
const m = MESSUNG[figur];
|
||||
const clipbilderProFrame = (tempo * CLIP_FPS) / FPS;
|
||||
const proSchritt = pxProClipbild(figur, hoehe) * m.schrittBilder;
|
||||
const schritte = Math.max(1, Math.round(Math.abs(nach - von) / proSchritt));
|
||||
const richtung = Math.sign(nach - von) || 1;
|
||||
const ziel = von + richtung * schritte * proSchritt;
|
||||
const dauer = (schritte * m.schrittBilder) / clipbilderProFrame;
|
||||
const ende = start + dauer;
|
||||
return {
|
||||
figur,
|
||||
start,
|
||||
ende,
|
||||
von,
|
||||
nach: ziel,
|
||||
tempo,
|
||||
clipStart: start - m.fuesseZusammen / clipbilderProFrame,
|
||||
spiegeln: richtung > 0,
|
||||
x: (f) => (f <= start ? von : f >= ende ? ziel : von + ((ziel - von) * (f - start)) / dauer),
|
||||
geht: (f) => f >= start && f < ende,
|
||||
};
|
||||
};
|
||||
45
remotion/src/folge08/Folge08.tsx
Normal file
45
remotion/src/folge08/Folge08.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
import {INTRO_OPTIONEN, Intro, OUTRO, Outro, introZeilen} from '../baukasten/buch';
|
||||
import {baueFolge, type SzenenEintrag} from '../baukasten/folge';
|
||||
import {mitKlang} from '../baukasten/geraeusche';
|
||||
import {TITEL, plane} from './plan';
|
||||
import {S2, Szene2} from './szene2';
|
||||
import {S3, Szene3} from './szene3';
|
||||
|
||||
// Folge 8 „Noas Laterne“ (läuft am Mi 11.11., St. Martin) – per Code aus der Figuren-Bibliothek,
|
||||
// Drehbuch drehbuecher/neu-noas-laterne/de.md. Gehen nur mit baukasten/gang.ts (keine rutschenden Füße).
|
||||
|
||||
const INTRO = plane('intro', introZeilen(TITEL), INTRO_OPTIONEN);
|
||||
|
||||
export const SZENEN: SzenenEintrag[] = [
|
||||
{
|
||||
name: 'Intro',
|
||||
datei: 'Folge08.tsx',
|
||||
plan: INTRO,
|
||||
K: mitKlang(() => <Intro plan={INTRO} titel={TITEL} posen={['pino_A3_lachen', 'noa_A3_lachen', 'lulu_A3_lachen']} />, INTRO, {
|
||||
toene: [{name: 'buch_auf', bei: 'buch', versatz: 0.7, lautstaerke: 0.35}],
|
||||
}),
|
||||
musik: {datei: 'musik/folge01/intro.mp3', lautstaerke: 0.35, rein: 0.2, raus: 1.2},
|
||||
},
|
||||
{
|
||||
name: 'Was wird das',
|
||||
datei: 'szene2.tsx',
|
||||
plan: S2,
|
||||
K: mitKlang(Szene2, S2, {atmo: 'zimmer', atmoLaut: 0.1, toene: [{name: 'rascheln', bei: 'bild0', versatz: 0.3, lautstaerke: 0.2, laenge: 1.5}]}),
|
||||
},
|
||||
{
|
||||
name: 'Es wird dunkel',
|
||||
datei: 'szene3.tsx',
|
||||
plan: S3,
|
||||
K: mitKlang(Szene3, S3, {atmo: 'garten_abend', atmoLaut: 0.12, toene: [{name: 'klick', bei: 'klick', versatz: 0.35, lautstaerke: 0.5}]}),
|
||||
},
|
||||
{
|
||||
name: 'Outro',
|
||||
datei: 'Folge08.tsx',
|
||||
plan: OUTRO,
|
||||
K: mitKlang(() => <Outro />, OUTRO, {toene: [{name: 'buch_zu', bei: 'buch', versatz: 3.2, lautstaerke: 0.45}]}),
|
||||
musik: {datei: 'musik/folge01/schlaflied.mp3', lautstaerke: 0.3, rein: 0.1, raus: 2},
|
||||
},
|
||||
];
|
||||
|
||||
export const F08 = baueFolge('F08', SZENEN);
|
||||
29
remotion/src/folge08/plan.ts
Normal file
29
remotion/src/folge08/plan.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import {SPRACHE, type FolgenTexte} from '../baukasten/sprache';
|
||||
import {plane as basis, type PlanOptionen, type Zeile} from '../baukasten/zeit';
|
||||
import DE from './texte/de.json';
|
||||
import ES from './texte/es.json';
|
||||
import FR from './texte/fr.json';
|
||||
import STIMMEN_DE from './stimmen/de.json';
|
||||
import STIMMEN_ES from './stimmen/es.json';
|
||||
import STIMMEN_FR from './stimmen/fr.json';
|
||||
|
||||
// Sprechtexte stehen nur in texte/<sprache>.json (Schlüssel „szene/zeile“),
|
||||
// die Szenen enthalten nur Ablauf und Bewegung. Aufnahmen je Sprache in
|
||||
// stimmen/<sprache>.json bzw. public/stimmen/folge08/<sprache>/ (erzeugt von
|
||||
// werkzeuge/stimmen.mts).
|
||||
|
||||
export const TEXTE: FolgenTexte = {de: DE, es: ES, fr: FR}[SPRACHE];
|
||||
const AUFNAHMEN = {de: STIMMEN_DE, es: STIMMEN_ES, fr: STIMMEN_FR}[SPRACHE];
|
||||
|
||||
export const TITEL = TEXTE.titel;
|
||||
|
||||
// Zeitplan einer Szene dieser Folge, mit den Texten der gewählten Sprache
|
||||
export const plane = (szene: string, zeilen: Zeile[], opt: PlanOptionen = {}) =>
|
||||
basis(
|
||||
zeilen.map((z) => {
|
||||
if (z.text !== undefined || !z.wer) return z;
|
||||
const text = TEXTE.zeilen[`${szene}/${z.id}`];
|
||||
return {...z, text: text ?? `[fehlt: ${szene}/${z.id}]`};
|
||||
}),
|
||||
{...opt, szene, aufnahmen: AUFNAHMEN, ordner: `stimmen/folge08/${SPRACHE}`},
|
||||
);
|
||||
1
remotion/src/folge08/stimmen/de.json
Normal file
1
remotion/src/folge08/stimmen/de.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
1
remotion/src/folge08/stimmen/es.json
Normal file
1
remotion/src/folge08/stimmen/es.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
1
remotion/src/folge08/stimmen/fr.json
Normal file
1
remotion/src/folge08/stimmen/fr.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
133
remotion/src/folge08/szene2.tsx
Normal file
133
remotion/src/folge08/szene2.tsx
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import React from 'react';
|
||||
import {interpolate, useCurrentFrame} from 'remotion';
|
||||
import {Figur, Kamera, POSEN_SKALA} from '../baukasten/figur';
|
||||
import {Blende, Vignette, atmen, huepfer, kamFahrt} from '../baukasten/helfer';
|
||||
import {Funkeln} from '../baukasten/natur';
|
||||
import {Ort} from '../baukasten/orte';
|
||||
import {Untertitel} from '../baukasten/untertitel';
|
||||
import {plane} from './plan';
|
||||
import {Laterne} from './teile';
|
||||
|
||||
// Szene 2: Was wird das? Nachmittags in Noas Zimmer. Noa bastelt am Tisch, Pino sitzt auf dem Tisch,
|
||||
// Lulu schaut durchs Fenster herein. Bei jeder Zahl klebt Noa einen Stern auf (Nahaufnahme), dann
|
||||
// den Papier-Igel; Lulu pustet den Kleber trocken. Aufbau wie Folge 2, Szene 6 (Noas Zimmer).
|
||||
|
||||
const N = 420; // Noas Größe im Zimmer (näher an der Kamera)
|
||||
const NOA_X = 760;
|
||||
const PINO_X = 1110;
|
||||
const TISCH_Y = 802;
|
||||
const LATERNE = {x: 950, y: TISCH_Y - 64};
|
||||
const LULU_X = 1440;
|
||||
|
||||
export const S2 = plane('s2', [
|
||||
{id: 'bild0', dauer: 2.5},
|
||||
{id: 'l1', wer: 'LULU'},
|
||||
{id: 'n1', wer: 'NOA'},
|
||||
{id: 'e1', wer: 'ERZ', pause: 3},
|
||||
{id: 'n2', wer: 'NOA'},
|
||||
{id: 'p1', wer: 'PINO'},
|
||||
{id: 'n3', wer: 'NOA'},
|
||||
{id: 'z1', wer: 'NOA', pause: 0.8},
|
||||
{id: 'z2', wer: 'NOA', pause: 0.8},
|
||||
{id: 'z3', wer: 'NOA', pause: 0.8},
|
||||
{id: 'z4', wer: 'NOA', pause: 0.8},
|
||||
{id: 'z5', wer: 'NOA', pause: 0.6},
|
||||
{id: 'p2', wer: 'PINO'},
|
||||
{id: 'n4', wer: 'NOA'},
|
||||
{id: 'igel', dauer: 1.8},
|
||||
{id: 'l2', wer: 'LULU'},
|
||||
{id: 'pusten', dauer: 2.6},
|
||||
{id: 'p3', wer: 'PINO'},
|
||||
{id: 'bild1', dauer: 1.4},
|
||||
]);
|
||||
|
||||
const ZAHLEN = ['z1', 'z2', 'z3', 'z4', 'z5'] as const;
|
||||
|
||||
export const Szene2: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const {z, dauer} = S2;
|
||||
|
||||
const sterne = ZAHLEN.filter((id) => f >= z[id].start).length;
|
||||
const igel = interpolate(f, [z.igel.start + 8, z.igel.start + 26], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const pusten = f >= z.pusten.start && f < z.p3.ende;
|
||||
const wind = pusten ? Math.sin(((f - z.pusten.start) / 30) * Math.PI * 1.6) : 0;
|
||||
const nah = f >= z.n3.ende - 6 && f < z.igel.ende; // Nahaufnahme beim Zählen und Igel-Kleben
|
||||
|
||||
const kam = nah
|
||||
? kamFahrt(f, [
|
||||
[z.n3.ende - 6, LATERNE.x - 10, LATERNE.y - 10, 2.6],
|
||||
[z.igel.ende, LATERNE.x, LATERNE.y - 4, 2.8],
|
||||
])
|
||||
: kamFahrt(f, [
|
||||
[0, 1000, 540, 1.0],
|
||||
[z.bild0.ende, 1080, 560, 1.12],
|
||||
[z.l1.ende, 1060, 600, 1.3],
|
||||
[z.e1.start, 960, 640, 1.45],
|
||||
[z.p1.start, 1030, 650, 1.55],
|
||||
[z.l2.start, 1140, 560, 1.2],
|
||||
[z.pusten.start + 10, 1080, 640, 1.5],
|
||||
[dauer, 1060, 640, 1.55],
|
||||
]);
|
||||
|
||||
// Pino zeigt bei jeder Zahl mit der Pfote: kleiner Hopser; beim Pusten wehen die Stacheln, er kichert
|
||||
const pinoHopser = ZAHLEN.reduce((a, id) => a + huepfer(f, z[id].start + 2, 10, 9), 0) + huepfer(f, z.p2.start, 16, 11) + huepfer(f, z.p3.start + 4, 12, 10);
|
||||
const pinoWeh = pusten ? 5 * wind : 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Kamera {...kam}>
|
||||
<Ort name="zimmer_himmel" />
|
||||
<Figur
|
||||
pose={[
|
||||
[0, 'lulu_A5_blick-links'],
|
||||
[z.n2.start, 'lulu_A4_staunen'],
|
||||
[z.n2.ende, 'lulu_A3_lachen'],
|
||||
[z.l2.start, 'lulu_A5_blick-links'],
|
||||
[z.p3.start, 'lulu_A3_lachen'],
|
||||
]}
|
||||
x={LULU_X}
|
||||
y={1180}
|
||||
hoehe={N * 2.05}
|
||||
schatten={false}
|
||||
drehung={pusten ? -2 + wind : 0}
|
||||
/>
|
||||
<Ort name="zimmer_wand" />
|
||||
{/* Wind vom Pusten: drei weiche Linien vom Fenster zur Laterne */}
|
||||
{pusten && (
|
||||
<svg viewBox="0 0 1920 1080" style={{position: 'absolute', inset: 0, width: 1920, height: 1080}}>
|
||||
{[0, 1, 2].map((i) => {
|
||||
const p = ((f - z.pusten.start) / 26 + i / 3) % 1;
|
||||
const x = interpolate(p, [0, 1], [1320, 1000]);
|
||||
return <path key={i} d={`M ${x} ${640 + i * 40} q -40 -16 -80 0 t -80 0`} stroke="#ffffff" strokeWidth={6} strokeLinecap="round" fill="none" opacity={Math.sin(p * Math.PI) * 0.7} />;
|
||||
})}
|
||||
</svg>
|
||||
)}
|
||||
<Figur pose="noa_B_sitzt" x={NOA_X} y={1000} hoehe={N / POSEN_SKALA.noa_B_sitzt} stauchY={atmen(f, 2)} drehung={nah ? Math.sin(f * 0.3) * 0.6 : 0} schatten={false} />
|
||||
<Ort name="zimmer_tisch" />
|
||||
<Laterne x={LATERNE.x} y={LATERNE.y} s={1.15} sterne={sterne} igel={igel} schwung={pusten ? 7 * wind : 0} stab={false} />
|
||||
{ZAHLEN.map((id, i) => (
|
||||
<Funkeln key={id} x={LATERNE.x + [-34, 22, 40, -6, -44][i] * 1.15} y={LATERNE.y + [-28, -40, 6, 30, 18][i] * 1.15} start={z[id].start} dauer={22} s={0.5} />
|
||||
))}
|
||||
<Figur
|
||||
pose={[
|
||||
[0, 'pino_A6_blick-rechts'],
|
||||
[z.l1.ende, 'pino_A5_blick-links'],
|
||||
[z.n2.start, 'pino_A4_staunen'],
|
||||
[z.p1.start, 'pino_A3_lachen'],
|
||||
[z.n3.ende, 'pino_A5_blick-links'],
|
||||
[z.p2.start, 'pino_A4_staunen'],
|
||||
[z.n4.start, 'pino_A3_lachen'],
|
||||
]}
|
||||
x={PINO_X}
|
||||
y={TISCH_Y + pinoHopser}
|
||||
hoehe={N * 0.4}
|
||||
drehung={pinoWeh}
|
||||
stauchY={atmen(f, 1)}
|
||||
/>
|
||||
</Kamera>
|
||||
<Vignette />
|
||||
<Untertitel zeilen={S2.liste} />
|
||||
<Blende dauer={dauer} rein={14} raus={10} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
100
remotion/src/folge08/szene3.tsx
Normal file
100
remotion/src/folge08/szene3.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import React from 'react';
|
||||
import {interpolate, useCurrentFrame} from 'remotion';
|
||||
import {ClipFigur, Figur, Kamera} from '../baukasten/figur';
|
||||
import {gang} from '../baukasten/gang';
|
||||
import {Blende, Vignette, atmen, kamFahrt} from '../baukasten/helfer';
|
||||
import {Funkeln} from '../baukasten/natur';
|
||||
import {BODEN} from '../baukasten/orte';
|
||||
import {Untertitel} from '../baukasten/untertitel';
|
||||
import {plane} from './plan';
|
||||
import {Abenddunkel, GartenAbend, Laterne, laterneInNoasHand} from './teile';
|
||||
|
||||
// Szene 3: Es wird dunkel. Abendgarten, rechts die Hauswand, Papa winkt im Fenster.
|
||||
// Noa knipst das Lämpchen an (kein Feuer) und singt den Laternenvers; die Laterne pendelt sanft.
|
||||
// Pino tippelt zu ihr (baukasten/gang.ts, Füße rutschen nicht). Lulu steht schon an der Hauswand –
|
||||
// sie geht nicht durchs Bild, weil ihr Geh-Clip kein echtes Gehen ist.
|
||||
|
||||
const NOA_X = 760;
|
||||
const LULU_X = 1640; // rechts vom Fenster, damit Papa zu sehen ist
|
||||
|
||||
export const S3 = plane('s3', [
|
||||
{id: 'bild0', dauer: 3.0},
|
||||
{id: 'e1', wer: 'ERZ'},
|
||||
{id: 'klick', dauer: 1.4},
|
||||
{id: 'e2', wer: 'ERZ'},
|
||||
{id: 'p1', wer: 'PINO'},
|
||||
{id: 'v1', wer: 'NOA'},
|
||||
{id: 'v2', wer: 'NOA'},
|
||||
{id: 'v3', wer: 'NOA'},
|
||||
{id: 'v4', wer: 'NOA', pause: 0.6},
|
||||
{id: 'e3', wer: 'ERZ', pause: 0.8},
|
||||
{id: 'w1', wer: 'NOA'},
|
||||
{id: 'w2', wer: 'NOA'},
|
||||
{id: 'w3', wer: 'NOA'},
|
||||
{id: 'w4', wer: 'NOA'},
|
||||
{id: 'bild1', dauer: 1.8},
|
||||
]);
|
||||
|
||||
export const Szene3: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const {z, dauer} = S3;
|
||||
|
||||
const an = z.klick.start + 12;
|
||||
const licht = interpolate(f, [an, an + 8], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const singt = f >= z.v1.start && f < z.w4.ende;
|
||||
const schwung = singt ? Math.sin((f - z.v1.start) * 0.09) * 7 : Math.sin(f * 0.05) * 2;
|
||||
const lat = laterneInNoasHand(NOA_X);
|
||||
|
||||
// Pino kommt beim Vers dicht an Noas Stiefel (ganze Schritte, Füße bleiben am Boden)
|
||||
const PINO = gang('pino', z.v1.start + 6, 300, NOA_X - 120, 2.5);
|
||||
|
||||
const kam = kamFahrt(f, [
|
||||
[0, 1000, 540, 1.0],
|
||||
[z.bild0.ende, 980, 560, 1.06],
|
||||
[z.klick.start, 760, 640, 1.55],
|
||||
[z.e2.ende, 720, 640, 1.7],
|
||||
[z.p1.start + 6, lat.x, lat.y, 2.3], // „Mein Igel leuchtet!“
|
||||
[z.p1.ende + 10, 820, 640, 1.35],
|
||||
[z.w1.start, 900, 600, 1.2],
|
||||
[dauer, 920, 600, 1.15],
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Kamera {...kam}>
|
||||
<GartenAbend papaWinkt={f < z.e1.ende ? f * 0.22 : 0} />
|
||||
<Figur pose={[[0, 'lulu_A5_blick-links'], [z.p1.start, 'lulu_A3_lachen']]} x={LULU_X} y={BODEN - 6} stauchY={atmen(f, 3, 0.006)} />
|
||||
<Figur
|
||||
pose={[
|
||||
[0, 'noa_A_laterne'],
|
||||
]}
|
||||
x={NOA_X}
|
||||
y={BODEN}
|
||||
stauchY={atmen(f, 2)}
|
||||
drehung={singt ? Math.sin((f - z.v1.start) * 0.045) * 1.5 : 0}
|
||||
/>
|
||||
<Laterne x={lat.x} y={lat.y} s={lat.s} licht={licht} schwung={schwung} stab={false} />
|
||||
<Funkeln x={lat.x} y={lat.y} start={an} dauer={30} s={0.8} />
|
||||
{PINO.geht(f) ? (
|
||||
<ClipFigur name="pino_C1_gehen-schleife" start={PINO.clipStart} tempo={PINO.tempo} schleife spiegeln={PINO.spiegeln} x={PINO.x(f)} y={BODEN} />
|
||||
) : (
|
||||
<Figur
|
||||
pose={[
|
||||
[0, 'pino_A6_blick-rechts'],
|
||||
[an, 'pino_A4_staunen'],
|
||||
[z.v1.start, 'pino_A6_blick-rechts'],
|
||||
[PINO.ende, 'pino_A3_lachen'],
|
||||
]}
|
||||
x={PINO.x(f)}
|
||||
y={BODEN}
|
||||
stauchY={atmen(f, 1)}
|
||||
/>
|
||||
)}
|
||||
<Abenddunkel licht={{x: lat.x, y: lat.y}} staerke={0.55 - 0.15 * licht} radius={380 + 260 * licht} />
|
||||
</Kamera>
|
||||
<Vignette />
|
||||
<Untertitel zeilen={S3.liste} />
|
||||
<Blende dauer={dauer} rein={14} raus={14} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
56
remotion/src/folge08/szene4.tsx
Normal file
56
remotion/src/folge08/szene4.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import {useCurrentFrame} from 'remotion';
|
||||
import {gang} from '../baukasten/gang';
|
||||
import {Blende, kamFahrt} from '../baukasten/helfer';
|
||||
import {Untertitel} from '../baukasten/untertitel';
|
||||
import {plane} from './plan';
|
||||
import {PLATZ, Wandszene} from './wand';
|
||||
|
||||
// Szene 4: Der große Schatten. An der Hauswand steht ein riesiger, weicher Giraffen-Schatten.
|
||||
// Pinos Stacheln gehen hoch, er rückt an Noas Stiefel. Noa hockt sich hin und stellt die Laterne ins Gras.
|
||||
// Lulu steht etwas im Dunkeln (Kinder dürfen sie ruhig schon sehen – so bleibt es freundlich).
|
||||
|
||||
export const S4 = plane('s4', [
|
||||
{id: 'bild0', dauer: 3.0},
|
||||
{id: 'e1', wer: 'ERZ', pause: 3},
|
||||
{id: 'p1', wer: 'PINO', leise: true},
|
||||
{id: 'n1', wer: 'NOA'},
|
||||
{id: 'e2', wer: 'ERZ'},
|
||||
{id: 'e3', wer: 'ERZ', pause: 3},
|
||||
]);
|
||||
|
||||
export const Szene4: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const {z, dauer} = S4;
|
||||
const erschrickt = z.bild0.start + 40;
|
||||
const PINO = gang('pino', erschrickt + 30, PLATZ.pino, PLATZ.noa + 90, 2.5);
|
||||
const hockt = f >= z.n1.start + 8;
|
||||
const angst = f >= erschrickt && f < z.e3.ende;
|
||||
const kam = kamFahrt(f, [
|
||||
[0, 960, 560, 1.0],
|
||||
[erschrickt, 900, 600, 1.12],
|
||||
[z.e1.start, 820, 720, 1.6], // Pinos Stacheln
|
||||
[z.p1.ende, 760, 700, 1.45],
|
||||
[z.e3.start, 1040, 520, 1.0], // der ganze Schatten
|
||||
[dauer, 1060, 520, 0.98],
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
<Wandszene
|
||||
f={f}
|
||||
kam={kam}
|
||||
laterne={hockt ? {inHand: false, licht: 1} : {inHand: true, licht: 1, schwung: Math.sin(f * 0.06) * 3}}
|
||||
noa={{pose: [[0, 'noa_A_laterne'], [z.n1.start + 8, 'noa_B_hocken']]}}
|
||||
pino={{
|
||||
pose: [[0, 'pino_A6_blick-rechts'], [erschrickt, 'pino_B2_erschrocken'], [z.e3.ende, 'pino_A4_staunen']],
|
||||
x: PINO.x(f),
|
||||
gang: PINO,
|
||||
drehung: angst ? Math.sin(f * 1.7) * 1.6 : 0,
|
||||
}}
|
||||
lulu={{pose: 'lulu_A4_staunen', hell: 0.55}}
|
||||
/>
|
||||
<Untertitel zeilen={S4.liste} />
|
||||
<Blende dauer={dauer} rein={14} raus={6} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
47
remotion/src/folge08/szene5.tsx
Normal file
47
remotion/src/folge08/szene5.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import React from 'react';
|
||||
import {interpolate, useCurrentFrame} from 'remotion';
|
||||
import {Blende, kamFahrt} from '../baukasten/helfer';
|
||||
import {Untertitel} from '../baukasten/untertitel';
|
||||
import {plane} from './plan';
|
||||
import {PLATZ, Wandszene} from './wand';
|
||||
|
||||
// Szene 5: Ach so! Noa hebt die Laterne hoch, jetzt fällt das Licht auf Lulu. Lulu nickt – der Schatten
|
||||
// nickt mit. Pinos Stacheln legen sich, alle lachen. Der Erzähler erklärt, wie ein Schatten entsteht.
|
||||
|
||||
export const S5 = plane('s5', [
|
||||
{id: 'bild0', dauer: 1.6},
|
||||
{id: 'l1', wer: 'LULU'},
|
||||
{id: 'p1', wer: 'PINO'},
|
||||
{id: 'lachen', dauer: 1.6},
|
||||
{id: 'e1', wer: 'ERZ'},
|
||||
{id: 'bild1', dauer: 1.0},
|
||||
]);
|
||||
|
||||
export const Szene5: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const {z, dauer} = S5;
|
||||
const hell = interpolate(f, [8, 30], [0.55, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
// Lulu nickt zweimal (vor „Huch!“), der Schatten macht es mit
|
||||
const nick = interpolate(f, [z.bild0.start + 18, z.bild0.ende + 12], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const nicken = Math.sin(nick * Math.PI * 4) * 3 * (nick > 0 && nick < 1 ? 1 : 0);
|
||||
const kam = kamFahrt(f, [
|
||||
[0, 1060, 520, 0.98],
|
||||
[z.l1.start, 1180, 560, 1.15],
|
||||
[z.p1.start, 1000, 560, 1.05],
|
||||
[dauer, 1000, 540, 1.0],
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
<Wandszene
|
||||
f={f}
|
||||
kam={kam}
|
||||
laterne={{inHand: true, licht: 1, schwung: Math.sin(f * 0.07) * 4}}
|
||||
noa={{pose: [[0, 'noa_B_hocken'], [6, 'noa_A_laterne']]}}
|
||||
pino={{pose: [[0, 'pino_A4_staunen'], [z.p1.start, 'pino_A3_lachen']], x: PLATZ.noa + 90}}
|
||||
lulu={{pose: [[0, 'lulu_A4_staunen'], [z.l1.start, 'lulu_A3_lachen']], hell, drehung: nicken}}
|
||||
/>
|
||||
<Untertitel zeilen={S5.liste} />
|
||||
<Blende dauer={dauer} rein={6} raus={6} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
66
remotion/src/folge08/szene6.tsx
Normal file
66
remotion/src/folge08/szene6.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import React from 'react';
|
||||
import {interpolate, useCurrentFrame} from 'remotion';
|
||||
import {gang} from '../baukasten/gang';
|
||||
import {Blende, huepfer, kamFahrt} from '../baukasten/helfer';
|
||||
import {BODEN} from '../baukasten/orte';
|
||||
import {Untertitel} from '../baukasten/untertitel';
|
||||
import {plane} from './plan';
|
||||
import {PLATZ, Wandszene} from './wand';
|
||||
|
||||
// Szene 6: Jetzt bin ich der Größte! Noa stellt die Laterne ins Gras, Pino tippelt ganz nah heran.
|
||||
// Sein Schatten wächst, bis er größer ist als Lulus. Lulu verbeugt sich vor dem großen Igel-Schatten.
|
||||
|
||||
export const S6 = plane('s6', [
|
||||
{id: 'p1', wer: 'PINO'},
|
||||
{id: 'n1', wer: 'NOA'},
|
||||
{id: 'hinstellen', dauer: 1.6},
|
||||
{id: 'e1', wer: 'ERZ', pause: 3},
|
||||
{id: 'wachsen', dauer: 2.4},
|
||||
{id: 'e2', wer: 'ERZ'},
|
||||
{id: 'p2', wer: 'PINO'},
|
||||
{id: 'verbeugen', dauer: 1.6},
|
||||
{id: 'l1', wer: 'LULU'},
|
||||
{id: 'p3', wer: 'PINO'},
|
||||
{id: 'bild1', dauer: 1.6},
|
||||
]);
|
||||
|
||||
const LATERNE = {x: 1010, y: PLATZ.laterneBoden.y};
|
||||
|
||||
export const Szene6: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const {z, dauer} = S6;
|
||||
const steht = f >= z.hinstellen.start + 8;
|
||||
// Pino geht während der Frage schon los und steht beim Wachsen dicht an der Laterne
|
||||
const PINO = gang('pino', z.e1.start + 20, PLATZ.noa + 90, LATERNE.x - 70, 2.5);
|
||||
const wachsen = interpolate(f, [z.wachsen.start, z.wachsen.ende], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const kPino = steht ? 1.25 + wachsen * 6.2 : undefined;
|
||||
const kam = kamFahrt(f, [
|
||||
[0, 1000, 560, 1.1],
|
||||
[z.hinstellen.start, 900, 640, 1.25],
|
||||
[z.wachsen.start, 980, 520, 1.0],
|
||||
[z.p2.start, 1000, 520, 1.0],
|
||||
[z.verbeugen.start, 1120, 560, 1.12],
|
||||
[dauer, 1040, 540, 1.02],
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
<Wandszene
|
||||
f={f}
|
||||
kam={kam}
|
||||
laterne={steht ? {inHand: false, licht: 1, x: LATERNE.x, y: LATERNE.y} : {inHand: true, licht: 1, schwung: Math.sin(f * 0.06) * 3}}
|
||||
noa={{pose: [[0, 'noa_A_laterne'], [z.hinstellen.start + 8, 'noa_B_hocken'], [z.p2.start, 'noa_A3_lachen']]}}
|
||||
pino={{
|
||||
pose: [[0, 'pino_B1_traurig'], [z.n1.start, 'pino_A4_staunen'], [z.e2.start, 'pino_A4_staunen'], [z.p2.start, 'pino_A3_lachen']],
|
||||
x: PINO.x(f),
|
||||
gang: PINO,
|
||||
k: kPino,
|
||||
y: BODEN + huepfer(f, z.p3.start, 18, 12) + huepfer(f, z.p3.start + 16, 14, 11),
|
||||
drehung: f >= z.p3.start ? Math.sin(f * 1.4) * 2 : 0, // Stacheln zittern vor Freude
|
||||
}}
|
||||
lulu={{pose: [[0, 'lulu_A3_lachen'], [z.verbeugen.start, 'lulu_A_hals-unten'], [z.p3.start, 'lulu_A3_lachen']]}}
|
||||
/>
|
||||
<Untertitel zeilen={S6.liste} />
|
||||
<Blende dauer={dauer} rein={6} raus={6} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
60
remotion/src/folge08/szene7.tsx
Normal file
60
remotion/src/folge08/szene7.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import React from 'react';
|
||||
import {interpolate, useCurrentFrame} from 'remotion';
|
||||
import {Blende, kamFahrt} from '../baukasten/helfer';
|
||||
import {Untertitel} from '../baukasten/untertitel';
|
||||
import {plane} from './plan';
|
||||
import {HandSchatten} from './teile';
|
||||
import {PLATZ, Wandszene} from './wand';
|
||||
|
||||
// Szene 7: Schattenspiel. Noa macht mit der Hand vor der Laterne einen Hasen, dann einen Igel.
|
||||
// Die Kinder machen mit (Pausen). Lulu streckt den Hals lang, ihr Schatten reicht bis zum Dach.
|
||||
|
||||
export const S7 = plane('s7', [
|
||||
{id: 'bild0', dauer: 1.2},
|
||||
{id: 'n1', wer: 'NOA'},
|
||||
{id: 'e1', wer: 'ERZ', pause: 3},
|
||||
{id: 'e2', wer: 'ERZ'},
|
||||
{id: 'hals', dauer: 2.2},
|
||||
{id: 'igel', dauer: 1.2},
|
||||
{id: 'p1', wer: 'PINO'},
|
||||
{id: 'e3', wer: 'ERZ', pause: 3},
|
||||
{id: 'e4', wer: 'ERZ'},
|
||||
{id: 'bild1', dauer: 1.2},
|
||||
]);
|
||||
|
||||
const LATERNE = {x: 1010, y: PLATZ.laterneBoden.y};
|
||||
|
||||
export const Szene7: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const {z, dauer} = S7;
|
||||
const hase = interpolate(f, [z.bild0.start + 6, z.bild0.ende, z.hals.start, z.hals.start + 10], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const igel = interpolate(f, [z.igel.start, z.igel.start + 12, dauer - 20, dauer - 6], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const strecken = interpolate(f, [z.hals.start, z.hals.start + 20, z.hals.ende - 10, z.hals.ende + 6], [1, 1.12, 1.12, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||||
const kam = kamFahrt(f, [
|
||||
[0, 1000, 520, 1.0],
|
||||
[z.n1.start, 1000, 470, 1.12],
|
||||
[z.hals.start, 1100, 500, 0.98],
|
||||
[z.igel.start, 1000, 480, 1.1],
|
||||
[dauer, 1000, 500, 1.06],
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
<Wandszene
|
||||
f={f}
|
||||
kam={kam}
|
||||
laterne={{inHand: false, licht: 1, x: LATERNE.x, y: LATERNE.y}}
|
||||
noa={{pose: [[0, 'noa_B_hocken'], [z.igel.start, 'noa_B_hocken']], x: PLATZ.noa + 140}}
|
||||
pino={{pose: [[0, 'pino_A4_staunen'], [z.p1.start, 'pino_A3_lachen']], x: LATERNE.x - 230}}
|
||||
lulu={{pose: 'lulu_A3_lachen', stauchY: strecken}}
|
||||
wandExtra={
|
||||
<>
|
||||
<HandSchatten art="hase" x={1060} y={440} s={1.1} deckkraft={0.42 * hase} wackeln={Math.sin(f * 0.25)} />
|
||||
<HandSchatten art="igel" x={1040} y={450} s={1.1} deckkraft={0.42 * igel} wackeln={Math.sin(f * 0.3)} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Untertitel zeilen={S7.liste} />
|
||||
<Blende dauer={dauer} rein={6} raus={14} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
219
remotion/src/folge08/teile.tsx
Normal file
219
remotion/src/folge08/teile.tsx
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
import React from 'react';
|
||||
import {Figur, NOA_HOEHE, type Posenfolge} from '../baukasten/figur';
|
||||
import {FIGUREN} from '../baukasten/posen';
|
||||
import {punkt} from '../baukasten/helfer';
|
||||
import {BODEN, Ort} from '../baukasten/orte';
|
||||
|
||||
// Bildteile für Folge 8 „Noas Laterne“: Abendgarten mit Hauswand, die gebastelte Laterne,
|
||||
// Schatten an der Wand und Hand-Schatten. Alles in Szenenkoordinaten (1920 × 1080).
|
||||
|
||||
// Hauswand rechts im Garten (Szene 3) bzw. als ganzer Hintergrund an der Wand (Szene 4–7)
|
||||
export const WAND = {x0: 1060, x1: 1980, oben: 170, unten: BODEN - 30};
|
||||
export const FENSTER = {x: 1180, y: 280, b: 230, h: 210};
|
||||
|
||||
// Abendgarten in der „blauen Stunde“: Nachtgarten aufgehellt, damit es freundlich und nicht unheimlich wirkt
|
||||
export const GartenAbend: React.FC<{wandVoll?: boolean; papaWinkt?: number}> = ({wandVoll = false, papaWinkt = 0}) => {
|
||||
const x0 = wandVoll ? -60 : WAND.x0;
|
||||
return (
|
||||
<>
|
||||
<div style={{position: 'absolute', inset: 0, filter: 'brightness(1.45) saturate(0.9)'}}>
|
||||
<Ort name="garten_nacht" />
|
||||
</div>
|
||||
<div style={{position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(110,130,200,0) 0%, rgba(150,150,200,0.12) 55%, rgba(240,180,140,0.22) 72%, rgba(60,70,110,0) 100%)'}} />
|
||||
<svg viewBox="0 0 1920 1080" style={{position: 'absolute', inset: 0, width: 1920, height: 1080, overflow: 'visible'}}>
|
||||
<defs>
|
||||
<linearGradient id="putz" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stopColor="#b9b5c9" />
|
||||
<stop offset="1" stopColor="#a7a2b6" />
|
||||
</linearGradient>
|
||||
<radialGradient id="fensterlicht">
|
||||
<stop offset="0" stopColor="#ffe3a0" stopOpacity="0.55" />
|
||||
<stop offset="1" stopColor="#ffe3a0" stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
{/* Wand mit Dachkante und Sockel */}
|
||||
<rect x={x0} y={WAND.oben} width={WAND.x1 - x0} height={WAND.unten - WAND.oben} fill="url(#putz)" />
|
||||
<rect x={x0 - 20} y={WAND.oben - 34} width={WAND.x1 - x0 + 40} height={40} fill="#5a4a5e" />
|
||||
<rect x={x0} y={WAND.unten - 34} width={WAND.x1 - x0} height={34} fill="#8f889c" />
|
||||
{!wandVoll && <rect x={x0 - 8} y={WAND.oben} width={16} height={WAND.unten - WAND.oben} fill="#9690a6" />}
|
||||
{/* erleuchtetes Fenster mit Papa als Umriss */}
|
||||
<ellipse cx={FENSTER.x + FENSTER.b / 2} cy={FENSTER.y + FENSTER.h / 2} rx={FENSTER.b * 1.1} ry={FENSTER.h * 1.1} fill="url(#fensterlicht)" />
|
||||
<rect x={FENSTER.x} y={FENSTER.y} width={FENSTER.b} height={FENSTER.h} rx={6} fill="#f6cf7a" stroke="#4b3f55" strokeWidth={12} />
|
||||
<g opacity={0.85}>
|
||||
<circle cx={FENSTER.x + 130} cy={FENSTER.y + 92} r={28} fill="#6b5560" />
|
||||
<path d={`M ${FENSTER.x + 82} ${FENSTER.y + FENSTER.h} C ${FENSTER.x + 86} ${FENSTER.y + 140}, ${FENSTER.x + 174} ${FENSTER.y + 140}, ${FENSTER.x + 178} ${FENSTER.y + FENSTER.h} Z`} fill="#6b5560" />
|
||||
{/* winkender Arm */}
|
||||
<g transform={`rotate(${-20 + 25 * Math.sin(papaWinkt)} ${FENSTER.x + 168} ${FENSTER.y + 150})`}>
|
||||
<rect x={FENSTER.x + 162} y={FENSTER.y + 70} width={16} height={84} rx={8} fill="#6b5560" />
|
||||
<circle cx={FENSTER.x + 170} cy={FENSTER.y + 66} r={13} fill="#6b5560" />
|
||||
</g>
|
||||
</g>
|
||||
<line x1={FENSTER.x + FENSTER.b / 2} y1={FENSTER.y} x2={FENSTER.x + FENSTER.b / 2} y2={FENSTER.y + FENSTER.h} stroke="#4b3f55" strokeWidth={8} />
|
||||
<line x1={FENSTER.x} y1={FENSTER.y + FENSTER.h / 2} x2={FENSTER.x + FENSTER.b} y2={FENSTER.y + FENSTER.h / 2} stroke="#4b3f55" strokeWidth={8} />
|
||||
<rect x={FENSTER.x - 18} y={FENSTER.y + FENSTER.h} width={FENSTER.b + 36} height={16} rx={4} fill="#6d6278" />
|
||||
{/* Haken für die Laterne (Szene 8) */}
|
||||
<path d={`M ${FENSTER.x + FENSTER.b + 40} ${FENSTER.y + 40} l 26 0 l 0 22`} stroke="#3f3646" strokeWidth={7} fill="none" strokeLinecap="round" />
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const HAKEN = {x: FENSTER.x + FENSTER.b + 66, y: FENSTER.y + 62};
|
||||
export const WAND_VOLL = {...WAND, x0: -60};
|
||||
|
||||
// Die gebastelte Laterne: gelbes Papier, fünf Glitzersterne, ein Papier-Igel, innen ein Lämpchen.
|
||||
// (x, y) = Mitte; sterne = wie viele schon aufgeklebt sind; igel 0–1; licht 0–1.
|
||||
const STERNE: [number, number][] = [
|
||||
[-34, -28],
|
||||
[22, -40],
|
||||
[40, 6],
|
||||
[-6, 30],
|
||||
[-44, 18],
|
||||
];
|
||||
const Stern: React.FC<{x: number; y: number; r: number; glanz: number}> = ({x, y, r, glanz}) => {
|
||||
const pkt = new Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => {
|
||||
const w = (Math.PI / 5) * i - Math.PI / 2;
|
||||
const rr = i % 2 ? r * 0.45 : r;
|
||||
return `${x + Math.cos(w) * rr},${y + Math.sin(w) * rr}`;
|
||||
})
|
||||
.join(' ');
|
||||
return <polygon points={pkt} fill={glanz > 0.5 ? '#fff6c2' : '#f2c84b'} stroke="#c9952a" strokeWidth={1.5} />;
|
||||
};
|
||||
|
||||
export const Laterne: React.FC<{x: number; y: number; s?: number; sterne?: number; igel?: number; licht?: number; schwung?: number; stab?: boolean}> = ({
|
||||
x,
|
||||
y,
|
||||
s = 1,
|
||||
sterne = 5,
|
||||
igel = 1,
|
||||
licht = 0,
|
||||
schwung = 0,
|
||||
stab = true,
|
||||
}) => (
|
||||
<div style={{position: 'absolute', left: x, top: y, width: 0, height: 0, transform: `scale(${s}) rotate(${schwung}deg)`, transformOrigin: '0 -70px'}}>
|
||||
{licht > 0 && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: -260,
|
||||
top: -260,
|
||||
width: 520,
|
||||
height: 520,
|
||||
borderRadius: '50%',
|
||||
background: `radial-gradient(closest-side, rgba(255,214,120,${0.55 * licht}), rgba(255,200,100,${0.18 * licht}) 55%, rgba(255,200,100,0))`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<svg width={200} height={260} viewBox="-100 -160 200 260" style={{position: 'absolute', left: -100, top: -160, overflow: 'visible'}}>
|
||||
<defs>
|
||||
<radialGradient id="papier" cx="0.45" cy="0.42" r="0.65">
|
||||
<stop offset="0" stopColor={licht > 0 ? '#fffbe2' : '#fbe79a'} />
|
||||
<stop offset="0.6" stopColor={licht > 0 ? '#ffd65c' : '#f2cf5c'} />
|
||||
<stop offset="1" stopColor={licht > 0 ? '#f0a531' : '#d9b03e'} />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
{stab && <line x1={0} y1={-70} x2={70} y2={-150} stroke="#7a5233" strokeWidth={6} strokeLinecap="round" />}
|
||||
<line x1={0} y1={-70} x2={0} y2={-56} stroke="#5a3d28" strokeWidth={2} />
|
||||
<rect x={-24} y={-60} width={48} height={10} rx={4} fill="#b8433a" />
|
||||
<ellipse cx={0} cy={0} rx={62} ry={56} fill="url(#papier)" />
|
||||
{[-0.62, -0.2, 0.2, 0.62].map((k) => (
|
||||
<path key={k} d={`M ${k * 62} -50 Q ${k * 100} 0 ${k * 62} 50`} stroke="#d39a2c" strokeWidth={1.6} fill="none" opacity={0.6} />
|
||||
))}
|
||||
{STERNE.slice(0, sterne).map(([sx, sy], i) => (
|
||||
<Stern key={i} x={sx} y={sy} r={11} glanz={licht} />
|
||||
))}
|
||||
{igel > 0 && (
|
||||
<g opacity={igel} transform="translate(8 2)">
|
||||
<ellipse cx={0} cy={0} rx={17} ry={12} fill="#8a5a36" />
|
||||
{[-14, -8, -2, 4, 10].map((dx) => (
|
||||
<path key={dx} d={`M ${dx} -8 l 3 -8 l 3 8`} fill="#6e4426" />
|
||||
))}
|
||||
<ellipse cx={-15} cy={3} rx={7} ry={6} fill="#e9c9a0" />
|
||||
<circle cx={-19} cy={3} r={1.8} fill="#2a1d14" />
|
||||
</g>
|
||||
)}
|
||||
<rect x={-24} y={50} width={48} height={10} rx={4} fill="#b8433a" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Weicher Schatten einer Figur an der Wand. Er steht hinter der Figur, ist um `k` vergrößert und
|
||||
// liegt vom Licht weg verschoben; je näher die Figur am Licht, desto größer (k aus `schattenGroesse`).
|
||||
// Weich, rund und bläulich-dunkel, ohne harten Rand (Drehbuch: groß, aber nicht unheimlich).
|
||||
export const schattenGroesse = (abstand: number) => Math.min(3.2, Math.max(1.05, 1 + 260 / Math.max(60, abstand)));
|
||||
|
||||
export const WandSchatten: React.FC<{
|
||||
pose: string | Posenfolge;
|
||||
x: number; // Fußpunkt der Figur
|
||||
licht: {x: number; y: number};
|
||||
hoehe: number;
|
||||
k: number;
|
||||
wand?: {x0: number; x1: number; oben: number; unten: number};
|
||||
deckkraft?: number;
|
||||
drehung?: number;
|
||||
stauchY?: number;
|
||||
}> = ({pose, x, licht, hoehe, k, wand = WAND, deckkraft = 0.42, drehung = 0, stauchY = 1}) => (
|
||||
<div style={{position: 'absolute', left: wand.x0, top: wand.oben, width: wand.x1 - wand.x0, height: wand.unten - wand.oben, overflow: 'hidden'}}>
|
||||
<div style={{position: 'absolute', left: -wand.x0, top: -wand.oben}}>
|
||||
<Figur
|
||||
pose={pose}
|
||||
x={licht.x + (x - licht.x) * (1 + (k - 1) * 0.55)}
|
||||
y={wand.unten - 6}
|
||||
hoehe={hoehe * k}
|
||||
schatten={false}
|
||||
drehung={drehung}
|
||||
stauchY={stauchY}
|
||||
deckkraft={deckkraft}
|
||||
filter="brightness(0) blur(5px)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Hand-Schatten an der Wand: Hase (zwei Ohren) und Igel (Faust mit gespreizten Fingern)
|
||||
export const HandSchatten: React.FC<{art: 'hase' | 'igel'; x: number; y: number; s?: number; deckkraft?: number; wackeln?: number}> = ({art, x, y, s = 1, deckkraft = 0.45, wackeln = 0}) => (
|
||||
<svg width={400} height={400} viewBox="-200 -200 400 400" style={{position: 'absolute', left: x - 200, top: y - 200, transform: `scale(${s})`, overflow: 'visible', opacity: deckkraft, filter: 'blur(4px)'}}>
|
||||
{art === 'hase' ? (
|
||||
<g fill="#1d1a2e">
|
||||
<ellipse cx={0} cy={40} rx={95} ry={70} />
|
||||
<ellipse cx={-40} cy={-60} rx={22} ry={80} transform={`rotate(${-12 + wackeln * 6} -40 0)`} />
|
||||
<ellipse cx={30} cy={-62} rx={22} ry={82} transform={`rotate(${10 - wackeln * 6} 30 0)`} />
|
||||
<ellipse cx={-80} cy={50} rx={40} ry={30} />
|
||||
<rect x={-20} y={90} width={70} height={120} rx={20} />
|
||||
</g>
|
||||
) : (
|
||||
<g fill="#1d1a2e">
|
||||
<ellipse cx={0} cy={40} rx={100} ry={64} />
|
||||
{[-70, -35, 0, 35, 70].map((dx, i) => (
|
||||
<ellipse key={dx} cx={dx * 1.05} cy={-30} rx={16} ry={56} transform={`rotate(${dx * 0.35 + (i % 2 ? wackeln * 4 : -wackeln * 4)} ${dx} 20)`} />
|
||||
))}
|
||||
<ellipse cx={-105} cy={60} rx={34} ry={24} />
|
||||
<rect x={-10} y={90} width={70} height={120} rx={20} />
|
||||
</g>
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Wo die Laterne in der Pose noa_A_laterne hängt, und wie groß unsere Laterne sein muss, um die orange
|
||||
// Laterne des Bildes ganz abzudecken (gemessen: 131 × 134 Bildpunkte, Mitte bei 73/423 im Bild, Bild bei 339/156)
|
||||
export const laterneInNoasHand = (noaX: number, noaY = BODEN) => ({
|
||||
...punkt('noa', noaX, noaY, 339 + 73.5, 156 + 423),
|
||||
s: (1.2 * NOA_HOEHE) / FIGUREN.noa.refHoehe,
|
||||
});
|
||||
|
||||
// Abend-Dämmerung über der ganzen Szene, aufgehellt um die Laterne (warmes Licht, Rest bläulich)
|
||||
export const Abenddunkel: React.FC<{licht: {x: number; y: number}; staerke?: number; radius?: number}> = ({licht, staerke = 0.4, radius = 500}) => (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: -400,
|
||||
top: -400,
|
||||
width: 2720,
|
||||
height: 1880,
|
||||
pointerEvents: 'none',
|
||||
background: `radial-gradient(circle at ${licht.x + 400}px ${licht.y + 400}px, rgba(255,210,130,0.12) 0px, rgba(30,34,70,0) ${radius * 0.45}px, rgba(24,28,62,${staerke}) ${radius * 1.6}px)`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
58
remotion/src/folge08/texte/de.json
Normal file
58
remotion/src/folge08/texte/de.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"titel": "Noas Laterne",
|
||||
"zeilen": {
|
||||
"s2/l1": "Was machst du da, Noa?",
|
||||
"s2/n1": "Rate mal! Es ist rund. Es hat einen Stab. Und innen drin leuchtet es.",
|
||||
"s2/e1": "Rund, mit einem Stab, und innen leuchtet es. Was bastelt Noa wohl?",
|
||||
"s2/n2": "Eine Laterne!",
|
||||
"s2/p1": "Eine Laterne! Mit was drauf?",
|
||||
"s2/n3": "Mit Sternen. Hilfst du mir zählen?",
|
||||
"s2/z1": "Eins …",
|
||||
"s2/z2": "zwei …",
|
||||
"s2/z3": "drei …",
|
||||
"s2/z4": "vier …",
|
||||
"s2/z5": "fünf!",
|
||||
"s2/p2": "Und ein Igel! Da muss ein Igel drauf!",
|
||||
"s2/n4": "Natürlich muss da ein Igel drauf.",
|
||||
"s2/l2": "Der Kleber ist noch nass. Ich puste!",
|
||||
"s2/p3": "Das kitzelt!",
|
||||
"s3/e1": "Es wird Abend in Noas Garten. Jetzt ist die richtige Zeit für eine Laterne.",
|
||||
"s3/e2": "Noas Laterne hat ein kleines Lämpchen. Kein Feuer. So kann nichts passieren.",
|
||||
"s3/p1": "Ooooh. Mein Igel leuchtet!",
|
||||
"s3/v1": "Mein kleines Licht, so rund und warm,",
|
||||
"s3/v2": "ich trag es durch den Garten.",
|
||||
"s3/v3": "Der Mond schaut schon, die Sterne auch,",
|
||||
"s3/v4": "die wollen auf uns warten.",
|
||||
"s3/e3": "Kannst du mitsingen?",
|
||||
"s3/w1": "Mein kleines Licht, so rund und warm,",
|
||||
"s3/w2": "ich trag es durch den Garten.",
|
||||
"s3/w3": "Der Mond schaut schon, die Sterne auch,",
|
||||
"s3/w4": "die wollen auf uns warten.",
|
||||
"s4/e1": "Schau mal, Pinos Stacheln gehen hoch. Wie fühlt sich Pino jetzt?",
|
||||
"s4/p1": "Noa … da ist was Großes an der Wand. Ich hab ein bisschen Angst.",
|
||||
"s4/n1": "Gut, dass du es mir sagst. Komm, wir schauen zusammen hin.",
|
||||
"s4/e2": "Wenn man Angst hat, darf man das sagen. Dann ist man nicht allein.",
|
||||
"s4/e3": "Der Schatten hat vier lange Beine. Und einen ganz langen Hals. Wem gehört wohl dieser Schatten?",
|
||||
"s5/l1": "Huch! Das bin ja ich!",
|
||||
"s5/p1": "Das ist ja Lulus Schatten! Der ist ja noch größer als Lulu!",
|
||||
"s5/e1": "Die Laterne leuchtet auf Lulu. Hinter Lulu kommt das Licht nicht hin. Da wird es dunkel. Das ist ein Schatten.",
|
||||
"s6/p1": "Mein Schatten ist so klein.",
|
||||
"s6/n1": "Warte mal. Ich hab eine Idee.",
|
||||
"s6/e1": "Pino geht ganz nah an die Laterne. Was meinst du: Wird sein Schatten jetzt größer oder kleiner?",
|
||||
"s6/e2": "Größer! Je näher man am Licht ist, desto größer wird der Schatten.",
|
||||
"s6/p2": "Jetzt bin ich der Größte von allen!",
|
||||
"s6/l1": "Guten Abend, großer Pino!",
|
||||
"s6/p3": "Einmal klein und einmal groß!",
|
||||
"s7/n1": "Schau mal, ein Hase!",
|
||||
"s7/e1": "Mach mal mit deiner Hand einen Hasen. Zwei Finger hoch für die Ohren.",
|
||||
"s7/e2": "Hallo, Hase!",
|
||||
"s7/p1": "Das bin ja ich!",
|
||||
"s7/e3": "Und jetzt einen Igel. Mach eine Faust und streck die Finger hoch, wie Stacheln.",
|
||||
"s7/e4": "Hallo, kleiner Igel!",
|
||||
"s8/p1": "Jetzt ist es gar nicht mehr dunkel.",
|
||||
"s8/n1": "Die Laterne passt auf dich auf. Gute Nacht, Pino.",
|
||||
"s8/e1": "Und bei dir? Ist da auch ein kleines Licht, wenn du schlafen gehst?",
|
||||
"s8/t1": "Tschüss! Bis bald!",
|
||||
"s8/p2": "… bis bald …"
|
||||
}
|
||||
}
|
||||
58
remotion/src/folge08/texte/es.json
Normal file
58
remotion/src/folge08/texte/es.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"titel": "El farolillo de Noa",
|
||||
"zeilen": {
|
||||
"s2/l1": "¿Qué haces, Noa?",
|
||||
"s2/n1": "¡Adivina! Es redondo. Tiene un palito. Y por dentro brilla.",
|
||||
"s2/e1": "Redondo, con un palito, y por dentro brilla. ¿Qué estará haciendo Noa?",
|
||||
"s2/n2": "¡Un farolillo!",
|
||||
"s2/p1": "¡Un farolillo! ¿Y qué lleva encima?",
|
||||
"s2/n3": "Estrellas. ¿Me ayudas a contar?",
|
||||
"s2/z1": "Una …",
|
||||
"s2/z2": "dos …",
|
||||
"s2/z3": "tres …",
|
||||
"s2/z4": "cuatro …",
|
||||
"s2/z5": "¡cinco!",
|
||||
"s2/p2": "¡Y un erizo! ¡Tiene que llevar un erizo!",
|
||||
"s2/n4": "Claro que tiene que llevar un erizo.",
|
||||
"s2/l2": "El pegamento todavía está mojado. ¡Yo soplo!",
|
||||
"s2/p3": "¡Me hace cosquillas!",
|
||||
"s3/e1": "Se hace de noche en el jardín de Noa. Ahora es el momento para un farolillo.",
|
||||
"s3/e2": "El farolillo de Noa tiene una lucecita. Nada de fuego. Así no puede pasar nada.",
|
||||
"s3/p1": "¡Ooooh! ¡Mi erizo brilla!",
|
||||
"s3/v1": "Mi farolillo, redondo y calentito,",
|
||||
"s3/v2": "lo llevo por el jardín.",
|
||||
"s3/v3": "La luna mira, y las estrellas,",
|
||||
"s3/v4": "nos esperan hasta el fin.",
|
||||
"s3/e3": "¿Cantas con nosotros?",
|
||||
"s3/w1": "Mi farolillo, redondo y calentito,",
|
||||
"s3/w2": "lo llevo por el jardín.",
|
||||
"s3/w3": "La luna mira, y las estrellas,",
|
||||
"s3/w4": "nos esperan hasta el fin.",
|
||||
"s4/e1": "Mira, a Pino se le levantan las púas. ¿Cómo se siente Pino ahora?",
|
||||
"s4/p1": "Noa… hay algo muy grande en la pared. Tengo un poquito de miedo.",
|
||||
"s4/n1": "Qué bien que me lo dices. Ven, vamos a mirar juntos.",
|
||||
"s4/e2": "Si tienes miedo, lo puedes decir. Así no estás solo.",
|
||||
"s4/e3": "La sombra tiene cuatro patas largas. Y un cuello larguísimo. ¿De quién será esta sombra?",
|
||||
"s5/l1": "¡Huy! ¡Pero si soy yo!",
|
||||
"s5/p1": "¡Es la sombra de Lulu! ¡Y es todavía más grande que Lulu!",
|
||||
"s5/e1": "El farolillo ilumina a Lulu. Detrás de Lulu, la luz no llega. Allí se pone oscuro. Eso es una sombra.",
|
||||
"s6/p1": "Mi sombra es muy pequeña.",
|
||||
"s6/n1": "Espera. Tengo una idea.",
|
||||
"s6/e1": "Pino se acerca mucho al farolillo. ¿Qué crees: su sombra se hará más grande o más pequeña?",
|
||||
"s6/e2": "¡Más grande! Cuanto más cerca de la luz, más grande es la sombra.",
|
||||
"s6/p2": "¡Ahora soy el más grande de todos!",
|
||||
"s6/l1": "¡Buenas noches, gran Pino!",
|
||||
"s6/p3": "¡Una vez pequeño y una vez grande!",
|
||||
"s7/n1": "¡Mira, un conejo!",
|
||||
"s7/e1": "Haz un conejo con tu mano. Dos dedos arriba para las orejas.",
|
||||
"s7/e2": "¡Hola, conejo!",
|
||||
"s7/p1": "¡Pero si soy yo!",
|
||||
"s7/e3": "Y ahora un erizo. Cierra la mano y estira los dedos hacia arriba, como púas.",
|
||||
"s7/e4": "¡Hola, erizo pequeño!",
|
||||
"s8/p1": "Ahora ya no está nada oscuro.",
|
||||
"s8/n1": "El farolillo te cuida. Buenas noches, Pino.",
|
||||
"s8/e1": "¿Y tú? ¿Tienes también una lucecita cuando te vas a dormir?",
|
||||
"s8/t1": "¡Adiós! ¡Hasta pronto!",
|
||||
"s8/p2": "…hasta pronto…"
|
||||
}
|
||||
}
|
||||
58
remotion/src/folge08/texte/fr.json
Normal file
58
remotion/src/folge08/texte/fr.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"titel": "La lanterne de Noa",
|
||||
"zeilen": {
|
||||
"s2/l1": "Qu’est-ce que tu fais, Noa ?",
|
||||
"s2/n1": "Devine ! C’est rond. Il y a un bâton. Et dedans, ça brille.",
|
||||
"s2/e1": "Rond, avec un bâton, et dedans, ça brille. Qu’est-ce que Noa fabrique ?",
|
||||
"s2/n2": "Une lanterne !",
|
||||
"s2/p1": "Une lanterne ! Avec quoi dessus ?",
|
||||
"s2/n3": "Avec des étoiles. Tu m’aides à compter ?",
|
||||
"s2/z1": "Une …",
|
||||
"s2/z2": "deux …",
|
||||
"s2/z3": "trois …",
|
||||
"s2/z4": "quatre …",
|
||||
"s2/z5": "cinq !",
|
||||
"s2/p2": "Et un hérisson ! Il faut un hérisson dessus !",
|
||||
"s2/n4": "Bien sûr qu’il faut un hérisson dessus.",
|
||||
"s2/l2": "La colle est encore mouillée. Je souffle !",
|
||||
"s2/p3": "Ça chatouille !",
|
||||
"s3/e1": "Le soir tombe dans le jardin de Noa. C’est le bon moment pour une lanterne.",
|
||||
"s3/e2": "La lanterne de Noa a une petite lampe. Pas de feu. Comme ça, il ne peut rien arriver.",
|
||||
"s3/p1": "Ooooh. Mon hérisson brille !",
|
||||
"s3/v1": "Ma petite lumière, ronde et douce,",
|
||||
"s3/v2": "je la promène au jardin.",
|
||||
"s3/v3": "La lune regarde, les étoiles aussi,",
|
||||
"s3/v4": "elles nous attendent en chemin.",
|
||||
"s3/e3": "Tu chantes avec nous ?",
|
||||
"s3/w1": "Ma petite lumière, ronde et douce,",
|
||||
"s3/w2": "je la promène au jardin.",
|
||||
"s3/w3": "La lune regarde, les étoiles aussi,",
|
||||
"s3/w4": "elles nous attendent en chemin.",
|
||||
"s4/e1": "Regarde, les piquants de Pino se dressent. Comment se sent Pino maintenant ?",
|
||||
"s4/p1": "Noa… il y a quelque chose de grand sur le mur. J’ai un petit peu peur.",
|
||||
"s4/n1": "C’est bien que tu me le dises. Viens, on regarde ensemble.",
|
||||
"s4/e2": "Quand on a peur, on a le droit de le dire. Comme ça, on n’est pas tout seul.",
|
||||
"s4/e3": "L’ombre a quatre longues pattes. Et un très long cou. À qui est cette ombre ?",
|
||||
"s5/l1": "Oh ! Mais c’est moi !",
|
||||
"s5/p1": "C’est l’ombre de Lulu ! Elle est encore plus grande que Lulu !",
|
||||
"s5/e1": "La lanterne éclaire Lulu. Derrière Lulu, la lumière n’arrive pas. Là, il fait sombre. C’est une ombre.",
|
||||
"s6/p1": "Mon ombre est toute petite.",
|
||||
"s6/n1": "Attends. J’ai une idée.",
|
||||
"s6/e1": "Pino s’approche tout près de la lanterne. À ton avis : son ombre va devenir plus grande ou plus petite ?",
|
||||
"s6/e2": "Plus grande ! Plus on est près de la lumière, plus l’ombre est grande.",
|
||||
"s6/p2": "Maintenant, c’est moi le plus grand de tous !",
|
||||
"s6/l1": "Bonsoir, grand Pino !",
|
||||
"s6/p3": "Une fois petit, une fois grand !",
|
||||
"s7/n1": "Regarde, un lapin !",
|
||||
"s7/e1": "Fais un lapin avec ta main. Deux doigts en l’air pour les oreilles.",
|
||||
"s7/e2": "Bonjour, petit lapin !",
|
||||
"s7/p1": "Mais c’est moi !",
|
||||
"s7/e3": "Et maintenant un hérisson. Ferme la main et lève les doigts, comme des piquants.",
|
||||
"s7/e4": "Bonjour, petit hérisson !",
|
||||
"s8/p1": "Maintenant, il ne fait plus du tout noir.",
|
||||
"s8/n1": "La lanterne veille sur toi. Bonne nuit, Pino.",
|
||||
"s8/e1": "Et toi ? Est-ce qu’il y a aussi une petite lumière quand tu vas dormir ?",
|
||||
"s8/t1": "Au revoir ! À bientôt !",
|
||||
"s8/p2": "…à bientôt…"
|
||||
}
|
||||
}
|
||||
61
remotion/src/folge08/wand.tsx
Normal file
61
remotion/src/folge08/wand.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import React from 'react';
|
||||
import {ClipFigur, Figur, Kamera, type Posenfolge} from '../baukasten/figur';
|
||||
import type {Gang} from '../baukasten/gang';
|
||||
import {Vignette} from '../baukasten/helfer';
|
||||
import {BODEN} from '../baukasten/orte';
|
||||
import {Abenddunkel, GartenAbend, Laterne, WAND_VOLL, WandSchatten, laterneInNoasHand} from './teile';
|
||||
|
||||
// Gemeinsame Bühne für Szene 4–7: die drei an der Hauswand, die Laterne als einzige Lichtquelle.
|
||||
// Jede Figur wirft ihren Schatten an die Wand; er nimmt dieselbe Pose, Drehung und Stauchung an
|
||||
// (nickt Lulu, nickt ihr Schatten mit). Feste Plätze, damit die Szenen zusammenpassen.
|
||||
|
||||
export const PLATZ = {noa: 700, pino: 880, lulu: 1330, laterneBoden: {x: 560, y: BODEN - 56}};
|
||||
|
||||
export type Buehne = {
|
||||
kam: {x: number; y: number; zoom: number};
|
||||
laterne: {inHand: boolean; x?: number; y?: number; licht: number; schwung?: number};
|
||||
noa: {pose: string | Posenfolge; x?: number};
|
||||
pino: {pose: string | Posenfolge; x: number; gang?: Gang; drehung?: number; y?: number; k?: number};
|
||||
lulu: {pose: string | Posenfolge; x?: number; hell?: number; drehung?: number; stauchY?: number; k?: number};
|
||||
wandExtra?: React.ReactNode; // z. B. Hand-Schatten
|
||||
f: number;
|
||||
};
|
||||
|
||||
export const Wandszene: React.FC<Buehne> = ({kam, laterne, noa, pino, lulu, wandExtra, f}) => {
|
||||
const noaX = noa.x ?? PLATZ.noa;
|
||||
const luluX = lulu.x ?? PLATZ.lulu;
|
||||
const hand = laterneInNoasHand(noaX);
|
||||
const lat = laterne.inHand ? {x: hand.x, y: hand.y} : {x: laterne.x ?? PLATZ.laterneBoden.x, y: laterne.y ?? PLATZ.laterneBoden.y};
|
||||
const s = hand.s;
|
||||
const kLulu = lulu.k ?? 1 + 220 / Math.max(120, Math.abs(luluX - lat.x));
|
||||
const kPino = pino.k ?? 1 + 220 / Math.max(80, Math.abs(pino.x - lat.x));
|
||||
const kNoa = 1 + 220 / Math.max(120, Math.abs(noaX - lat.x));
|
||||
const hell = lulu.hell ?? 1;
|
||||
const pinoGeht = pino.gang?.geht(f);
|
||||
return (
|
||||
<>
|
||||
<Kamera {...kam}>
|
||||
<GartenAbend wandVoll />
|
||||
{/* Schatten an der Wand (hinter den Figuren) */}
|
||||
<WandSchatten pose={lulu.pose} x={luluX} licht={lat} hoehe={340 * 2.05} k={kLulu} wand={WAND_VOLL} drehung={lulu.drehung} stauchY={lulu.stauchY} />
|
||||
<WandSchatten pose={noa.pose} x={noaX} licht={lat} hoehe={340} k={kNoa} wand={WAND_VOLL} deckkraft={0.3} />
|
||||
<WandSchatten pose={pinoGeht ? 'pino_A6_blick-rechts' : pino.pose} x={pino.x} licht={lat} hoehe={340 * 0.4} k={kPino} wand={WAND_VOLL} drehung={pino.drehung} />
|
||||
{wandExtra}
|
||||
<Figur pose={lulu.pose} x={luluX} y={BODEN - 6} drehung={lulu.drehung} stauchY={lulu.stauchY} filter={hell < 1 ? `brightness(${hell})` : undefined} />
|
||||
<Figur pose={noa.pose} x={noaX} y={BODEN} />
|
||||
{laterne.inHand ? (
|
||||
<Laterne x={lat.x} y={lat.y} s={s} licht={laterne.licht} schwung={laterne.schwung ?? 0} stab={false} />
|
||||
) : (
|
||||
<Laterne x={lat.x} y={lat.y} s={s} licht={laterne.licht} stab={false} />
|
||||
)}
|
||||
{pinoGeht && pino.gang ? (
|
||||
<ClipFigur name="pino_C1_gehen-schleife" start={pino.gang.clipStart} tempo={pino.gang.tempo} schleife spiegeln={pino.gang.spiegeln} x={pino.gang.x(f)} y={BODEN} />
|
||||
) : (
|
||||
<Figur pose={pino.pose} x={pino.x} y={pino.y ?? BODEN} drehung={pino.drehung} />
|
||||
)}
|
||||
<Abenddunkel licht={lat} staerke={0.42} radius={640} />
|
||||
</Kamera>
|
||||
<Vignette />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -329,7 +329,7 @@ const Garten: React.FC = () => {
|
|||
))}
|
||||
<KorbVorne />
|
||||
<Figur pose={NOA} x={NOA_X} y={BODEN + (singt ? wipp(0.3, 10 * ruhig) : 0)} drehung={singt ? schwank(0, 3 * ruhig) : 0} blende={3} />
|
||||
<Figur pose={PINO} x={PINO_X} y={BODEN + (singt ? wipp(0, 12 * ruhig) : 0) - pinoHuepf} drehung={singt ? schwank(1, 4 * ruhig) : 0} stauchY={1 - 0.03 * schlag} blende={6} />
|
||||
<Figur pose={PINO} x={PINO_X} y={BODEN + (singt ? wipp(0, 12 * ruhig) : 0) + pinoHuepf} drehung={singt ? schwank(1, 4 * ruhig) : 0} stauchY={1 - 0.03 * schlag} blende={6} />
|
||||
<Ruf text={LIED.mampf} x={kopf.x - 140} y={kopf.y - 40} start={MAMPF} dauer={30} groesse={80} farbe="#c8362c" />
|
||||
<Funkeln x={kopf.x - 60} y={kopf.y + 20} start={s(T.antwort + 0.6)} dauer={50} s={1.2} />
|
||||
</Kamera>
|
||||
|
|
|
|||
25
remotion/src/szenen/GangTest.tsx
Normal file
25
remotion/src/szenen/GangTest.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import {AbsoluteFill, useCurrentFrame} from 'remotion';
|
||||
import {ClipFigur, Figur} from '../baukasten/figur';
|
||||
import {gang} from '../baukasten/gang';
|
||||
|
||||
// Prüfstand für baukasten/gang.ts: Noa und Pino gehen nach rechts, die Füße dürfen nicht rutschen.
|
||||
const NOA = gang('noa', 10, 300, 1500);
|
||||
const PINO = gang('pino', 10, 300, 900, 2.5);
|
||||
|
||||
export const GangTest: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const figur = (g: typeof NOA, y: number) =>
|
||||
g.geht(f) ? (
|
||||
<ClipFigur name={`${g.figur}_C1_gehen-schleife`} start={g.clipStart} tempo={g.tempo} schleife spiegeln={g.spiegeln} x={g.x(f)} y={y} />
|
||||
) : (
|
||||
<Figur pose={`${g.figur}_A3_lachen`} x={g.x(f)} y={y} />
|
||||
);
|
||||
return (
|
||||
<AbsoluteFill style={{background: '#ffffff'}}>
|
||||
{figur(NOA, 500)}
|
||||
{figur(PINO, 950)}
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
export const GANGTEST_DAUER = Math.ceil(Math.max(NOA.ende, PINO.ende)) + 20;
|
||||
Loading…
Reference in a new issue