diff --git a/remotion/src/baukasten/figur.tsx b/remotion/src/baukasten/figur.tsx index 76f2128..256ae4b 100644 --- a/remotion/src/baukasten/figur.tsx +++ b/remotion/src/baukasten/figur.tsx @@ -119,8 +119,16 @@ export const ClipFigur: React.FC = ({name, start, hoehe, tempo = 1, s // Clip noch nicht freigestellt: Standbild der Figur als Ersatz const figurName = name.split('_')[0] as FigurName; if (name.endsWith('_C1_gehen-schleife')) { - const bild = 1 + (Math.floor(((frame - start) * tempo * 6) / fps) % 4); - return ; + // vier Geh-Bilder, jeweils kurz ineinander übergeblendet + const schritt = fps / (6 * tempo); + const t = Math.max(0, frame - start); + const k = Math.floor(t / schritt); + const bild = (n: number) => `${figurName}_A7-${1 + (((n % 4) + 4) % 4)}_gehen`; + const folge: Posenfolge = [ + [(k - 1) * schritt, bild(k - 1)], + [k * schritt, bild(k)], + ]; + return ; } return ; } diff --git a/remotion/src/baukasten/orte.tsx b/remotion/src/baukasten/orte.tsx index 663e15b..07be8a2 100644 --- a/remotion/src/baukasten/orte.tsx +++ b/remotion/src/baukasten/orte.tsx @@ -315,7 +315,7 @@ export const GartenHerbstSVG: React.FC<{abend?: boolean}> = ({abend = false}) => ); })} - + ); diff --git a/remotion/src/folge01/Folge01.tsx b/remotion/src/folge01/Folge01.tsx index 8005425..aa27d54 100644 --- a/remotion/src/folge01/Folge01.tsx +++ b/remotion/src/folge01/Folge01.tsx @@ -1,8 +1,14 @@ import React from 'react'; import {INTRO_OPTIONEN, Intro, OUTRO, Outro, introZeilen} from '../baukasten/buch'; import {baueFolge, type SzenenEintrag} from '../baukasten/folge'; +import {LIED, Lied} from './lied'; import {TITEL, plane} from './plan'; import {S2, Szene2} from './szene2'; +import {S3, Szene3} from './szene3'; +import {S4, Szene4} from './szene4'; +import {S5, Szene5} from './szene5'; +import {S6, Szene6} from './szene6'; +import {S7, Szene7} from './szene7'; // Folge 1 „Zu klein, zu groß“ – wie Folge 2 per Code aus der Figuren-Bibliothek. @@ -11,7 +17,13 @@ const INTRO = plane('intro', introZeilen(TITEL), INTRO_OPTIONEN); export const SZENEN: SzenenEintrag[] = [ {name: 'Intro', datei: 'Folge01.tsx', plan: INTRO, K: () => }, {name: 'Wer raschelt da', datei: 'szene2.tsx', plan: S2, K: Szene2}, - {name: 'Outro', datei: 'Folge01.tsx', plan: OUTRO, K: () => }, + {name: 'Der Apfel ist zu hoch', datei: 'szene3.tsx', plan: S3, K: Szene3}, + {name: 'Ein Baum mit Flecken', datei: 'szene4.tsx', plan: S4, K: Szene4}, + {name: 'Die Blume ist zu tief', datei: 'szene5.tsx', plan: S5, K: Szene5}, + {name: 'Zusammen gehts', datei: 'szene6.tsx', plan: S6, K: Szene6}, + {name: 'Lied', datei: 'lied.tsx', plan: LIED, K: Lied}, + {name: 'Pino ist muede', datei: 'szene7.tsx', plan: S7, K: Szene7}, + {name: 'Outro', datei: 'Folge01.tsx', plan: OUTRO, K: () => }, ]; export const F01 = baueFolge('F01', SZENEN); diff --git a/remotion/src/folge01/lied.tsx b/remotion/src/folge01/lied.tsx new file mode 100644 index 0000000..f1c1763 --- /dev/null +++ b/remotion/src/folge01/lied.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import {random, useCurrentFrame} from 'remotion'; +import {Figur, Kamera} from '../baukasten/figur'; +import {Blende, Vignette} from '../baukasten/helfer'; +import {Blume, Laubhaufen} from '../baukasten/natur'; +import {BODEN, Ort} from '../baukasten/orte'; +import {SCHRIFT, Untertitel} from '../baukasten/untertitel'; +import {TEXTE, plane} from './plan'; +import {HAUFEN_X} from './szene2'; + +// Lied „Einer klein und einer groß“: Refrain (alle), Strophe (Erzählstimme), Refrain. +// Die drei stehen nebeneinander – klein, mittel, groß – und wippen im Takt. + +const TAKT = 19; // Frames pro Schlag (ca. 95 bpm) + +export const LIED = plane( + 'lied', + [ + {id: 'r1', wer: 'ALLE', dauer: 3.2}, + {id: 'r2', wer: 'ALLE', dauer: 3.2}, + {id: 'r3', wer: 'ALLE', dauer: 3.2}, + {id: 'r4', wer: 'ALLE', dauer: 3.6}, + {id: 's1', wer: 'ERZ', dauer: 3.2}, + {id: 's2', wer: 'ERZ', dauer: 3.2}, + {id: 's3', wer: 'ERZ', dauer: 3.2}, + {id: 's4', wer: 'ERZ', dauer: 3.6}, + {id: 'w1', wer: 'ALLE', dauer: 3.2}, + {id: 'w2', wer: 'ALLE', dauer: 3.2}, + {id: 'w3', wer: 'ALLE', dauer: 3.2}, + {id: 'w4', wer: 'ALLE', dauer: 3.6}, + ], + {vorlauf: 1.0, nachlauf: 1.0}, +); + +const Note: React.FC<{x: number; y: number; s: number; farbe: string}> = ({x, y, s, farbe}) => ( + + + + + +); + +export const Lied: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = LIED; + const schlag = Math.abs(Math.sin((f * Math.PI) / TAKT)); + const wipp = (phase: number, h: number) => -Math.abs(Math.sin((f * Math.PI) / TAKT + phase)) * h; + const schwank = (phase: number, g: number) => Math.sin((f * Math.PI) / (2 * TAKT) + phase) * g; + // Strophe: „Lulu holt ihn mit dem Mund“ / „Pino pflückt sie“ + const luluPose: [number, string][] = [ + [0, 'lulu_A3_lachen'], + [z.s2.start, 'lulu_A_apfel'], + [z.s3.start, 'lulu_A3_lachen'], + ]; + const pinoPose: [number, string][] = [ + [0, 'pino_A3_lachen'], + [z.s4.start, 'pino_A_hinterbeine'], + [z.w1.start, 'pino_A3_lachen'], + ]; + const noaPose: [number, string][] = [ + [0, 'noa_A3_lachen'], + [z.r4.start, 'noa_A_arme-hoch'], + [z.s1.start, 'noa_A3_lachen'], + [z.w4.start, 'noa_A_arme-hoch'], + ]; + return ( + <> + + + + + + + {f >= z.s4.start && f < z.w1.start && } + + {new Array(8).fill(0).map((_, i) => { + const t = ((f + i * 25) % 200) / 200; + const x = 200 + random(`n${i}`) * 1500 + Math.sin(t * 6 + i) * 30; + return ( +
+ +
+ ); + })} +
+ ♫ {TEXTE.lied} ♫ +
+ + + + + ); +}; diff --git a/remotion/src/folge01/szene2.tsx b/remotion/src/folge01/szene2.tsx index 85987c5..4507091 100644 --- a/remotion/src/folge01/szene2.tsx +++ b/remotion/src/folge01/szene2.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {useCurrentFrame} from 'remotion'; import {kurve} from '../bewegung'; -import {ClipFigur, Figur, Geher, Kamera} from '../baukasten/figur'; +import {Figur, Geher, Kamera} from '../baukasten/figur'; import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt, ruck} from '../baukasten/helfer'; import {Apfel, Blaetterregen, Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur'; import {BODEN, Ort} from '../baukasten/orte'; @@ -12,7 +12,7 @@ import {plane} from './plan'; // Lage im Herbstgarten (gilt für alle Szenen der Folge) export const HAUFEN_X = 1180; // Laubhaufen -export const APFEL = {x: 560, y: 205}; // der letzte Apfel, ganz oben im Baum +export const APFEL = {x: 600, y: 390}; // der letzte Apfel: zu hoch für Noa, Lulu reicht mit dem Maul hin export const BLUME_X = 1560; // blaue Blume am Zaun export const BLUME_Y = 805; export const NOA_X = 960; // Noa neben dem Haufen @@ -101,27 +101,27 @@ export const Szene2: React.FC = () => { // Pino: als Stachelkugel im Haufen, kommt hoch, öffnet sich bei bild4 const auf = z.bild4.start; // Einroll-Clip rückwärts - const AUF_FRAMES = 75; // 121 Clip-Bilder (24 fps) bei Tempo 2 - const oeffnet = f >= auf && f < auf + AUF_FRAMES; + const AUF_FRAMES = 30; + const oeffnen = f >= auf && f < auf + AUF_FRAMES ? Math.sin(((f - auf) / AUF_FRAMES) * Math.PI) : 0; const zuNoa = f >= auf; // die Bilder schauen nach rechts, Noa sitzt links const pinoY = kurve(f, [ [schreck - 10, PINO_HAUFEN_Y + 140], [schreck + 6, PINO_HAUFEN_Y], [auf + 20, PINO_HAUFEN_Y], - [auf + AUF_FRAMES, PINO_HAUFEN_Y - 40], + [auf + AUF_FRAMES, PINO_HAUFEN_Y - 22], ]); const pinoDa = f >= schreck - 10; const pinoPose: [number, string][] = [ [0, 'pino_A_kugel'], - [auf + AUF_FRAMES, 'pino_ref_sitzend'], - [z.p1.start, 'pino_B_blick-nach-oben'], + [auf + 4, 'pino_ref_sitzend'], + [z.p1.start, 'pino_A6_blick-rechts'], [z.p1.start + 40, 'pino_A4_staunen'], [z.p2.start, 'pino_B_blick-nach-oben'], [z.p2.ende - 10, 'pino_A3_lachen'], ]; // Stacheln zittern vor Angst, solange er eine Kugel ist const angst = f < auf ? Math.sin(f * 1.7) * 1.5 : 0; - const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, schreck); + const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, schreck) * (1 + 0.06 * oeffnen); const kam = kamFahrt(f, [ [0, 960, 540, 1.0], @@ -143,12 +143,7 @@ export const Szene2: React.FC = () => { - {pinoDa && - (oeffnet ? ( - - ) : ( - - ))} + {pinoDa && } {/* Im Haufen steht Noa hinter den vorderen Blättern */} {imHaufen && } diff --git a/remotion/src/folge01/szene3.tsx b/remotion/src/folge01/szene3.tsx new file mode 100644 index 0000000..301f664 --- /dev/null +++ b/remotion/src/folge01/szene3.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import {useCurrentFrame} from 'remotion'; +import {kurve} from '../bewegung'; +import {Figur, Geher, Kamera} from '../baukasten/figur'; +import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer'; +import {Apfel, Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur'; +import {BODEN, Ort} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {APFEL, BLUME_X, BLUME_Y, HAUFEN_X} from './szene2'; + +// Szene 3: Der Apfel ist zu hoch. Pino hüpft, Noa streckt sich – beide zu klein. +// Pino-Bilder schauen nach rechts (zum Apfel); zu Noa (links) wird er gespiegelt. + +export const PINO_BAUM_X = 500; // Pino unter dem Apfel +export const NOA_BAUM_X = 330; // Noa am untersten Ast + +export const S3 = plane('s3', [ + {id: 'bild0', dauer: 2.6}, + {id: 'p1', wer: 'PINO'}, + {id: 'bild1', dauer: 2.4}, + {id: 'p2', wer: 'PINO', dauer: 2.2}, + {id: 'p3', wer: 'PINO'}, + {id: 'n1', wer: 'NOA'}, + {id: 'bild2', dauer: 2.8}, + {id: 'n2', wer: 'NOA'}, + {id: 'e1', wer: 'ERZ'}, + {id: 'e2', wer: 'ERZ', pause: 3}, +]); + +export const Szene3: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S3; + + // Pino: schaut hoch, stellt sich auf die Hinterbeine, hüpft zweimal, ist traurig + const hopp1 = z.p2.start + 2; + const hopp2 = z.p2.start + 32; + const pinoPose: [number, string][] = [ + [0, 'pino_B_blick-nach-oben'], + [z.bild1.start, 'pino_A_hinterbeine'], + [z.p3.start, 'pino_B1_traurig'], + [z.n1.start + 8, 'pino_A4_staunen'], + [z.n2.start, 'pino_B1_traurig'], + [z.e1.start + 20, 'pino_B_blick-nach-oben'], + ]; + const zuNoa = f >= z.n1.start && f < z.e1.start + 20; + const strecken = kurve(f, [ + [z.bild1.start, 0], + [z.bild1.start + 20, 1], + [z.p3.start, 1], + [z.p3.start + 12, 0], + ]); + const pinoY = BODEN + huepfer(f, hopp1, 46, 16) + huepfer(f, hopp2, 46, 16); + const pinoStauch = atmen(f, 1, 0.01) * (1 + 0.05 * strecken) * hopfStauch(f, hopp1 + 16) * hopfStauch(f, hopp2 + 16); + + // Noa kommt von links, streckt sich zum untersten Ast – reicht nicht + const noaAn = z.n1.start + 6; + const noaX = kurve(f, [ + [z.p3.start - 70, -150], + [noaAn, NOA_BAUM_X], + ]); + const noaGeht = f >= z.p3.start - 70 && f < noaAn; + const noaDa = f >= z.p3.start - 70; + const greifen = z.bild2.start + 6; + const noaPose: [number, string][] = [ + [0, 'noa_A3_lachen'], + [greifen, 'noa_A_arm-hoch-winken'], + [z.n2.start, 'noa_B1_traurig'], + [z.e1.start + 20, 'noa_A4_staunen'], + [z.e2.start, 'noa_A3_lachen'], + ]; + // auf die Zehenspitzen: strecken und zweimal kurz hochfedern + const zehen = kurve(f, [ + [greifen, 0], + [greifen + 14, 1], + [z.n2.start - 6, 1], + [z.n2.start + 8, 0], + ]); + const noaY = BODEN + huepfer(f, greifen + 22, 14, 12) + huepfer(f, greifen + 46, 14, 12); + + const kam = kamFahrt(f, [ + [0, PINO_BAUM_X + 60, 830, 2.0], + [z.p1.start, 560, 560, 1.7], + [z.p1.ende, 560, 560, 1.7], + [z.bild1.start + 10, PINO_BAUM_X + 40, 800, 1.9], + [z.p3.ende, 520, 740, 1.6], + [z.n1.ende, 470, 720, 1.5], + [z.bild2.start + 30, 500, 600, 1.35], + [z.e1.start, 560, 560, 1.2], + [dauer, 600, 560, 1.15], + ]); + + return ( + <> + + + + + + {noaDa && + (noaGeht ? ( + + ) : ( + + ))} + + + + + + + + ); +}; diff --git a/remotion/src/folge01/szene4.tsx b/remotion/src/folge01/szene4.tsx new file mode 100644 index 0000000..8822579 --- /dev/null +++ b/remotion/src/folge01/szene4.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import {useCurrentFrame} from 'remotion'; +import {kurve} from '../bewegung'; +import {Figur, Geher, Kamera} from '../baukasten/figur'; +import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer'; +import {Apfel, Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur'; +import {BODEN, Ort} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {APFEL, BLUME_X, BLUME_Y, HAUFEN_X} from './szene2'; +import {NOA_BAUM_X, PINO_BAUM_X} from './szene3'; + +// Szene 4: Ein Baum mit Flecken? Lulu kommt, pflückt den Apfel und reicht ihn Pino. +// Lulus Bilder schauen nach links (zu Noa und Pino), Pinos nach rechts (zu Lulu). + +// Lulus Maul mit dem Apfel (lulu_A_apfel) liegt 149 px links und 542 px über dem Fußpunkt: +// so steht sie, dass sie den Apfel am Baum erreicht. +export const LULU_APFEL_X = APFEL.x + 149; +// Mit gesenktem Hals (lulu_A_hals-unten) liegt das Maul 244 px links vom Fußpunkt, knapp über dem Boden. +const LULU_UNTEN_X = 840; +const LULU_WARTE_X = 930; // bis zum Pflücken etwas rechts vom Apfel +const MAUL_UNTEN = {x: LULU_UNTEN_X - 244, y: BODEN - 22}; +const APFEL_BODEN = {x: 578, y: BODEN - 14}; + +export const S4 = plane('s4', [ + {id: 'bild0', dauer: 3.4}, + {id: 'n1', wer: 'NOA'}, + {id: 'p1', wer: 'PINO'}, + {id: 'bild1', dauer: 3.2}, + {id: 'l1', wer: 'LULU'}, + {id: 'e1', wer: 'ERZ'}, + {id: 'e2', wer: 'ERZ'}, + {id: 'p2', wer: 'PINO'}, + {id: 'l2', wer: 'LULU'}, + {id: 'l3', wer: 'LULU'}, + {id: 'bild2', dauer: 3.4}, + {id: 'p3', wer: 'PINO'}, + {id: 'l4', wer: 'LULU'}, + {id: 'p4', wer: 'PINO'}, +]); + +export const Szene4: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S4; + + // Lulu stakst langsam von rechts herein (tapp, tapp) + const luluAn = z.bild0.ende + 10; + const essen = z.bild2.start + 10; + const unten = z.bild2.start + 52; + const ablegen = z.bild2.ende - 6; + const luluX = kurve(f, [ + [0, 1650], + [luluAn, LULU_WARTE_X], + [essen - 16, LULU_WARTE_X], + [essen + 2, LULU_APFEL_X], + [unten, LULU_APFEL_X], + [unten + 14, LULU_UNTEN_X], + ]); + const luluGeht = f < luluAn; + const luluPose: [number, string][] = [ + [0, 'lulu_A4_staunen'], + [z.l1.start, 'lulu_A3_lachen'], + [z.l2.start, 'lulu_A5_blick-links'], + [z.l3.start + 20, 'lulu_A4_staunen'], + [essen, 'lulu_A_apfel'], + [unten, 'lulu_A_hals-unten'], + [z.p4.ende - 4, 'lulu_A3_lachen'], + ]; + + // Der Apfel: am Baum → in Lulus Maul (im Bild lulu_A_apfel) → am Maul nach unten → vor Pino + const apfelAmBaum = f < essen + 6; + const apfelAmMaul = f >= unten + 8; + const apfel = apfelAmMaul + ? { + x: kurve(f, [ + [ablegen, MAUL_UNTEN.x], + [ablegen + 12, APFEL_BODEN.x], + ]), + y: kurve(f, [ + [ablegen, MAUL_UNTEN.y], + [ablegen + 12, APFEL_BODEN.y], + ]), + } + : APFEL; + + // Pino staunt, freut sich; Stacheln halb hoch bei „noch viel größer“ (kurz aufplustern) + const pinoPose: [number, string][] = [ + [0, 'pino_A4_staunen'], + [z.bild1.start + 40, 'pino_B_blick-nach-oben'], + [z.p2.start, 'pino_A4_staunen'], + [z.l2.start, 'pino_B_blick-nach-oben'], + [z.p3.start, 'pino_A4_staunen'], + [z.p4.start, 'pino_A3_lachen'], + ]; + const pinoY = BODEN + huepfer(f, z.p4.start + 10, 34, 14) + huepfer(f, z.p4.start + 34, 22, 12); + const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, z.p2.start, 16) * hopfStauch(f, z.p4.start + 24); + + const noaPose: [number, string][] = [ + [0, 'noa_A4_staunen'], + [z.n1.start, 'noa_B_zeigen'], + [z.p1.start + 10, 'noa_A3_lachen'], + [z.bild1.start + 40, 'noa_A4_staunen'], + [z.l2.start, 'noa_A3_lachen'], + ]; + const noaY = BODEN + huepfer(f, z.l1.ende, 16, 10); + + const kam = kamFahrt(f, [ + [0, 1000, 860, 2.2], + [luluAn, 820, 850, 2.1], + [z.n1.start, 620, 820, 1.7], + [z.bild1.start, 700, 840, 1.9], + [z.bild1.start + 60, 660, 380, 1.9], + [z.l1.ende, 660, 400, 1.8], + [z.e1.start + 20, 700, 560, 1.05], + [z.e2.ende, 700, 560, 1.05], + [z.p2.start + 10, 520, 800, 1.8], + [z.l2.start + 10, 640, 430, 1.6], + [z.l3.ende, 620, 420, 1.7], + [unten - 10, 620, 440, 1.6], + [unten + 30, 570, 800, 1.8], + [z.p3.start, 560, 780, 1.7], + [dauer, 600, 700, 1.4], + ]); + + return ( + <> + + + + {apfelAmBaum && } + {luluGeht ? ( + + ) : ( + + )} + + + + {apfelAmMaul && } + + + + + + + ); +}; diff --git a/remotion/src/folge01/szene5.tsx b/remotion/src/folge01/szene5.tsx new file mode 100644 index 0000000..1d988b2 --- /dev/null +++ b/remotion/src/folge01/szene5.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import {useCurrentFrame} from 'remotion'; +import {kurve} from '../bewegung'; +import {Figur, Kamera} from '../baukasten/figur'; +import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer'; +import {Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur'; +import {BODEN, Ort} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {BLUME_X, BLUME_Y, HAUFEN_X} from './szene2'; + +// Szene 5: Die Blume ist zu tief. Lulu beugt sich, wackelt, kommt nicht hinunter. +// Lulu steht rechts der Blume und schaut nach links; Pino und Noa stehen links davon. + +export const LULU_BLUME_X = 1760; +export const PINO_BLUME_X = 950; +export const NOA_BLUME_X = 820; + +export const S5 = plane('s5', [ + {id: 'bild0', dauer: 2.6}, + {id: 'l1', wer: 'LULU'}, + {id: 'l2', wer: 'LULU'}, + {id: 'bild1', dauer: 3.6}, + {id: 'l3', wer: 'LULU'}, + {id: 'l4', wer: 'LULU'}, + {id: 'bild2', dauer: 1.8}, + {id: 'e1', wer: 'ERZ'}, + {id: 'e2', wer: 'ERZ', pause: 3}, + {id: 'n1', wer: 'NOA'}, +]); + +// Kichern wie in Folge 2: kleine Hüpfer mit Wiegen, schwillt an und ab +export const kichern = (f: number, a: number, e: number, phase = 0) => { + const huelle = f > a && f < e ? Math.sin(((f - a) / (e - a)) * Math.PI) : 0; + const p = (f - a) * (0.42 + 0.08 * Math.sin((f - a) * 0.05)) + phase; + return { + y: -Math.abs(Math.sin(p)) * 7 * huelle, + stauch: 1 - 0.035 * Math.abs(Math.cos(p)) * huelle, + dreh: (Math.sin(p * 0.5) * 2.5 + 2) * huelle, + }; +}; + +export const Szene5: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S5; + + // Lulu neigt sich zur Blume (nach links), wackelt, richtet sich wieder auf + const beugA = z.bild1.start; + const beugE = z.l4.ende; + const neigen = kurve(f, [ + [beugA, 0], + [beugA + 36, 1], + [beugE - 10, 1], + [beugE + 18, 0], + ]); + const wackeln = neigen * (Math.sin(f * 0.55) * 3 + Math.sin(f * 0.23) * 2); + const luluDreh = -24 * neigen + wackeln * 1.4; + const luluPose: [number, string][] = [ + [0, 'lulu_A4_staunen'], + [z.l1.start + 10, 'lulu_A3_lachen'], + [beugA + 10, 'lulu_A1_augen-zu'], + [z.l3.start, 'lulu_B2_erschrocken'], + [z.bild2.start, 'lulu_B1_traurig'], + [z.n1.start + 20, 'lulu_A4_staunen'], + ]; + + // Pino und Noa kichern, während Lulu wackelt + const kA = z.bild1.start + 20; + const kE = z.l4.ende; + const kp = kichern(f, kA, kE); + const kn = kichern(f, kA, kE, 1.3); + const pinoPose: [number, string][] = [ + [0, 'pino_A4_staunen'], + [kA, 'pino_A3_lachen'], + [z.bild2.start, 'pino_A2_augen-halb'], + [z.e2.start, 'pino_A4_staunen'], + [z.n1.start + 10, 'pino_A3_lachen'], + ]; + const noaPose: [number, string][] = [ + [0, 'noa_A4_staunen'], + [kA, 'noa_A3_lachen'], + [z.bild2.start, 'noa_A4_staunen'], + [z.n1.start, 'noa_A3_lachen'], + [z.n1.start + 30, 'noa_B_zeigen'], + ]; + + const kam = kamFahrt(f, [ + [0, BLUME_X, 760, 2.4], + [z.l1.start, 1650, 560, 1.4], + [z.l2.ende, 1600, 560, 1.3], + [z.bild1.start + 40, 1560, 600, 1.3], + [z.bild2.start, 1500, 620, 1.25], + [z.e2.start, 1480, 640, 1.3], + [z.n1.start, 900, 760, 1.8], + [dauer, 910, 770, 1.9], + ]); + + return ( + <> + + + + + + + + + + + + + + ); +}; diff --git a/remotion/src/folge01/szene6.tsx b/remotion/src/folge01/szene6.tsx new file mode 100644 index 0000000..f89fbcc --- /dev/null +++ b/remotion/src/folge01/szene6.tsx @@ -0,0 +1,153 @@ +import React from 'react'; +import {useCurrentFrame} from 'remotion'; +import {kurve} from '../bewegung'; +import {Figur, Geher, Kamera} from '../baukasten/figur'; +import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer'; +import {Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur'; +import {BODEN, Ort} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {BLUME_X, BLUME_Y, HAUFEN_X} from './szene2'; +import {LULU_BLUME_X, NOA_BLUME_X, PINO_BLUME_X} from './szene5'; + +// Szene 6: Zusammen geht's. Pino pflückt die Blume, Noa hält sie hoch, Lulu riecht daran. + +const PINO_AN_BLUME = {x: BLUME_X - 40, y: BLUME_Y + 50}; +// Lulus Nase in lulu_A1 (Augen zu, schaut nach links): Leinwand (597, 167), Fuß (714, 734) +const LULU_NASE = {x: LULU_BLUME_X - 117, y: BODEN - 6 - 567}; + +export const S6 = plane('s6', [ + {id: 'bild0', dauer: 5.6}, + {id: 'bild1', dauer: 2.6}, + {id: 'l1', wer: 'LULU'}, + {id: 'e1', wer: 'ERZ'}, + {id: 'e2', wer: 'ERZ'}, + {id: 'e3', wer: 'ERZ'}, + {id: 'e4', wer: 'ERZ'}, +]); + +export const Szene6: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S6; + + // Pino tippelt zur Blume (nach rechts), pflückt sie, tippelt zurück zu Noa + const hin = z.bild0.start + 4; + const da = hin + 80; + const pflueck = da + 6; + const zurueck = pflueck + 34; + const wieder = zurueck + 80; + // Weg vor dem Laubhaufen entlang (weiter vorn = tiefer im Bild) + const VORN = {x: HAUFEN_X + 10, y: BODEN + 70}; + const mitteHin = (hin + da) / 2; + const mitteZurueck = (zurueck + wieder) / 2; + const pinoX = kurve(f, [ + [hin, PINO_BLUME_X], + [mitteHin, VORN.x], + [da, PINO_AN_BLUME.x], + [zurueck, PINO_AN_BLUME.x], + [mitteZurueck, VORN.x], + [wieder, NOA_BLUME_X + 120], + ]); + const pinoBoden = kurve(f, [ + [hin, BODEN], + [mitteHin, VORN.y], + [da, PINO_AN_BLUME.y], + [zurueck, PINO_AN_BLUME.y], + [mitteZurueck, VORN.y], + [wieder, BODEN], + ]); + const pinoGehtHin = f >= hin && f < da; + const pinoGehtZurueck = f >= zurueck && f < wieder; + const pinoPose: [number, string][] = [ + [0, 'pino_A4_staunen'], + [da, 'pino_A_hinterbeine'], + [wieder, 'pino_A_hinterbeine'], + [z.l1.start, 'pino_A3_lachen'], + ]; + const s = 136 / 571; // Pino: Bildschirm-Pixel je Leinwand-Pixel + const pfoten = {x: pinoX + 4, y: pinoBoden - 210 * s}; + + // Die Blume: am Zaun → in Pinos Pfoten → zu Noa, die sie hochhält + const geben = z.bild1.start + 6; + const hoch = geben + 26; + // rechte Hand in noa_A_arme-hoch: Leinwand (800, 190), Fuß (675, 700), Maßstab 340/535 + const NOA_HAENDE = {x: NOA_BLUME_X + 79, y: BODEN - 310}; + let blume: {x: number; y: number; s: number} = {x: BLUME_X, y: BLUME_Y, s: 0.8}; + if (f >= pflueck && f < geben) blume = {x: pfoten.x, y: pfoten.y + 20, s: 0.55}; + if (f >= geben) + blume = { + x: kurve(f, [ + [geben, NOA_BLUME_X + 120], + [hoch, NOA_HAENDE.x], + ]), + y: kurve(f, [ + [geben, BODEN - 20], + [hoch, NOA_HAENDE.y], + ]), + s: 0.62, + }; + + const noaPose: [number, string][] = [ + [0, 'noa_A3_lachen'], + [geben, 'noa_A_arme-hoch'], + [z.e1.start, 'noa_A_arme-hoch'], + [z.e3.start, 'noa_A3_lachen'], + ]; + const blumeImBild = f < z.e3.start; + + // Nahaufnahme: Lulu riecht an der Blume (die Blume kommt von unten ins Bild) + const nahA = z.l1.start - 20; + const nahE = z.l1.ende + 10; + const nah = f >= nahA && f < nahE; + const luluPose: [number, string][] = [ + [0, 'lulu_A4_staunen'], + [nahA, 'lulu_A1_augen-zu'], + [z.l1.start + 30, 'lulu_A3_lachen'], + ]; + const nahBlumeY = kurve(f, [ + [nahA, LULU_NASE.y + 260], + [nahA + 24, LULU_NASE.y + 92], + ]); + + const kam = kamFahrt(f, [ + [0, 1150, 760, 1.4], + [da, 1450, 780, 1.7], + [zurueck, 1350, 780, 1.6], + [wieder, 960, 740, 1.6], + [hoch, 960, 660, 1.5], + [nahA - 1, 960, 660, 1.5], + [nahA, LULU_NASE.x - 20, LULU_NASE.y + 60, 3.2], + [nahE - 1, LULU_NASE.x - 20, LULU_NASE.y + 60, 3.0], + [nahE, 1500, 560, 1.1], + [z.e1.start + 10, LULU_BLUME_X - 150, 420, 1.9], + [z.e2.start, PINO_BLUME_X + 80, 840, 2.0], + [z.e3.start, NOA_BLUME_X, 700, 1.8], + [z.e4.start, 1300, 600, 1.15], + [dauer, 1300, 600, 1.1], + ]); + + return ( + <> + + + + {f < pflueck && } + + + {pinoGehtHin ? ( + + ) : pinoGehtZurueck ? ( + + ) : ( + + )} + {f >= pflueck && blumeImBild && !nah && } + {nah && } + + + + + + + ); +}; diff --git a/remotion/src/folge01/szene7.tsx b/remotion/src/folge01/szene7.tsx new file mode 100644 index 0000000..46f4c6a --- /dev/null +++ b/remotion/src/folge01/szene7.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import {useCurrentFrame} from 'remotion'; +import {kurve} from '../bewegung'; +import {Figur, Kamera} from '../baukasten/figur'; +import {Blende, Vignette, atmen, kamFahrt} from '../baukasten/helfer'; +import {Apfel, Blaetterwind, Laubhaufen, Zzz} from '../baukasten/natur'; +import {BODEN, Ort} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {HAUFEN_X} from './szene2'; + +// Szene 7: Pino ist müde. Abendlicht, die drei sitzen unter dem Apfelbaum, Pino gähnt. +// Lulu liegt links und schaut nach rechts, Noa sitzt rechts und schaut nach links, Pino in der Mitte. + +const LULU_X = 600; +const PINO_X = 840; +const NOA_X = 1080; + +export const S7 = plane('s7', [ + {id: 'bild0', dauer: 2.6}, + {id: 'p1', wer: 'PINO'}, + {id: 'n1', wer: 'NOA'}, + {id: 'e1', wer: 'ERZ'}, + {id: 'e2', wer: 'ERZ', pause: 3}, + {id: 'e3', wer: 'ERZ'}, + {id: 'e4', wer: 'ERZ'}, + {id: 't1', wer: 'ALLE', leise: true}, + {id: 'bild1', dauer: 1.6}, +]); + +export const Szene7: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S7; + + // Pino knabbert, gähnt lang (strecken), wird schläfrig, schläft am Ende ein + const gaehnA = z.p1.start; + const gaehn = kurve(f, [ + [gaehnA, 0], + [gaehnA + 14, 1], + [gaehnA + 48, 1], + [gaehnA + 64, 0], + ]); + const pinoPose: [number, string][] = [ + [0, 'pino_A3_lachen'], + [gaehnA + 2, 'pino_A4_staunen'], + [gaehnA + 60, 'pino_A2_augen-halb'], + [z.e3.start, 'pino_A2_augen-halb'], + [z.t1.start + 10, 'pino_A1_augen-zu'], + [z.bild1.start, 'pino_B3_schlafen'], + ]; + const pinoStauch = atmen(f, 1, f > z.t1.start ? 0.02 : 0.01) * (1 + 0.07 * gaehn); + + const kam = kamFahrt(f, [ + [0, 840, 620, 1.25], + [z.p1.start, PINO_X, 760, 1.7], + [z.n1.ende, 900, 720, 1.5], + [z.e2.start, 860, 700, 1.4], + [z.t1.start, 860, 720, 1.5], + [dauer, PINO_X, 790, 2.0], + ]); + + return ( + <> + + + + + + + + + + + + + + + ); +};