Folge 1 komplett in Remotion: Szenen 3–7 und Lied; Szene 2 Pino blickt zu Noa, Aufrollen ohne Clip; Apfel tiefer (für Lulu erreichbar); Geh-Ersatz mit Überblendung

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lara 2026-11-02 13:57:57 +01:00
parent 3c5620ef6e
commit 88d2ae0f01
10 changed files with 733 additions and 18 deletions

View file

@ -119,8 +119,16 @@ export const ClipFigur: React.FC<ClipProps> = ({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 <Figur pose={`${figurName}_A7-${bild}_gehen`} hoehe={hoehe} blende={1} {...lage} />;
// 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 <Figur pose={folge} frame={t} hoehe={hoehe} blende={Math.max(1, Math.min(4, schritt / 2))} {...lage} />;
}
return <Figur pose={ersatz ?? ({pino: 'pino_ref_sitzend', noa: 'noa_ref_stehend', lulu: 'lulu_A6_blick-rechts'} as const)[figurName]} hoehe={hoehe} {...lage} />;
}

View file

@ -315,7 +315,7 @@ export const GartenHerbstSVG: React.FC<{abend?: boolean}> = ({abend = false}) =>
);
})}
</g>
<AlterBaum x={340} y={905} s={0.95} abend={abend} apfel={[232, -770]} />
<AlterBaum x={340} y={905} s={0.95} abend={abend} apfel={[274, -576]} />
<Korn staerke={0.18} />
</svg>
);

View file

@ -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: () => <Intro plan={INTRO} titel={TITEL} posen={['pino_A3_lachen', 'noa_A3_lachen', 'lulu_A3_lachen']} />},
{name: 'Wer raschelt da', datei: 'szene2.tsx', plan: S2, K: Szene2},
{name: 'Outro', datei: 'Folge01.tsx', plan: OUTRO, K: () => <Outro posen={['pino_A3_lachen', 'noa_A_arm-hoch-winken', 'lulu_A3_lachen']} />},
{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: () => <Outro posen={['pino_B3_schlafen', 'noa_A_arm-hoch-winken', 'lulu_A3_lachen']} />},
];
export const F01 = baueFolge('F01', SZENEN);

View file

@ -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}) => (
<svg width={60} height={80} viewBox="0 0 60 80" style={{position: 'absolute', left: x, top: y, transform: `scale(${s})`}}>
<ellipse cx={18} cy={64} rx={15} ry={11} fill={farbe} transform="rotate(-20 18 64)" />
<rect x={30} y={8} width={6} height={58} fill={farbe} />
<path d="M 36 8 C 50 18, 56 30, 46 44 C 50 30, 44 24, 36 22 Z" fill={farbe} />
</svg>
);
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 (
<>
<Kamera x={1000} y={600} zoom={1.3}>
<Ort name="garten_herbst" />
<Laubhaufen x={HAUFEN_X + 380} y={BODEN + 6} />
<Figur pose={luluPose} x={1300} y={BODEN} drehung={schwank(0.5, 2.2)} stauchY={1 - 0.012 * schlag} blende={8} />
<Figur pose={noaPose} x={980} y={BODEN + wipp(0.3, 10)} drehung={schwank(0, 3)} blende={8} />
<Figur pose={pinoPose} x={760} y={BODEN + wipp(0, 16)} drehung={schwank(1, 4)} stauchY={1 - 0.03 * schlag} blende={8} />
{f >= z.s4.start && f < z.w1.start && <Blume x={764} y={BODEN - 40} s={0.5} />}
</Kamera>
{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 (
<div key={i} style={{opacity: Math.sin(t * Math.PI) * 0.9}}>
<Note x={x} y={700 - t * 600} s={0.8 + random(`ns${i}`) * 0.6} farbe={['#c8642c', '#3f5a8a', '#5f8a4a', '#a0526b'][i % 4]} />
</div>
);
})}
<div style={{position: 'absolute', top: 60, left: 0, right: 0, textAlign: 'center', fontFamily: SCHRIFT, fontWeight: 600, fontSize: 40, color: '#8a6a4a', opacity: Math.min(1, f / 20)}}>
♫ {TEXTE.lied} ♫
</div>
<Vignette />
<Untertitel zeilen={LIED.liste} />
<Blende dauer={dauer} rein={12} raus={12} />
</>
);
};

View file

@ -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 = () => {
<Ort name="garten_herbst" />
<Apfel x={APFEL.x} y={APFEL.y} s={0.9} />
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} />
{pinoDa &&
(oeffnet ? (
<ClipFigur name="pino_C7_einrollen" start={auf} tempo={2} rueckwaerts spiegeln x={HAUFEN_X + 10} y={pinoY} schatten={false} ersatz="pino_ref_sitzend" />
) : (
<Figur pose={pinoPose} spiegeln={zuNoa} x={HAUFEN_X + 10} y={pinoY} stauchY={pinoStauch} drehung={angst} schatten={false} blende={8} />
))}
{pinoDa && <Figur pose={pinoPose} spiegeln={zuNoa} x={HAUFEN_X + 10} y={pinoY} stauchY={pinoStauch} drehung={angst} schatten={false} blende={14} />}
{/* Im Haufen steht Noa hinter den vorderen Blättern */}
{imHaufen && <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} schatten={false} />}
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} wackeln={wackeln} flach={flach} />

View file

@ -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 (
<>
<Kamera {...kam}>
<Ort name="garten_herbst" />
<Apfel x={APFEL.x} y={APFEL.y} s={0.9} r={Math.sin(f * 0.05) * 4} />
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} />
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} />
{noaDa &&
(noaGeht ? (
<Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.5} />
) : (
<Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * (1 + 0.04 * zehen)} blende={10} />
))}
<Figur pose={pinoPose} spiegeln={zuNoa} x={PINO_BAUM_X} y={pinoY} stauchY={pinoStauch} blende={10} />
</Kamera>
<Blaetterwind anzahl={4} />
<Vignette />
<Untertitel zeilen={S3.liste} />
<Blende dauer={dauer} rein={12} raus={0} />
</>
);
};

View file

@ -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 (
<>
<Kamera {...kam}>
<Ort name="garten_herbst" />
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} />
{apfelAmBaum && <Apfel x={APFEL.x} y={APFEL.y} s={0.9} r={Math.sin(f * 0.05) * 4} />}
{luluGeht ? (
<Geher figur="lulu" x={luluX} y={BODEN - 6} tempo={0.6} />
) : (
<Figur pose={luluPose} x={luluX} y={BODEN - 6} stauchY={atmen(f, 3, 0.006)} blende={14} />
)}
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} />
<Figur pose={noaPose} x={NOA_BAUM_X} y={noaY} stauchY={atmen(f, 2)} blende={10} />
<Figur pose={pinoPose} x={PINO_BAUM_X} y={pinoY} stauchY={pinoStauch} blende={10} />
{apfelAmMaul && <Apfel x={apfel.x} y={apfel.y} s={0.62} />}
</Kamera>
<Blaetterwind anzahl={4} />
<Vignette />
<Untertitel zeilen={S4.liste} />
<Blende dauer={dauer} rein={12} raus={0} />
</>
);
};

View file

@ -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 (
<>
<Kamera {...kam}>
<Ort name="garten_herbst" />
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} />
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} r={Math.sin(f * 0.07) * 3} />
<Figur pose={luluPose} x={LULU_BLUME_X} y={BODEN - 6} drehung={luluDreh} stauchY={atmen(f, 3, 0.006)} blende={12} />
<Figur pose={noaPose} x={NOA_BLUME_X} y={BODEN + kn.y + huepfer(f, z.n1.start, 18, 10)} stauchY={atmen(f, 2) * kn.stauch} drehung={kn.dreh} blende={10} />
<Figur pose={pinoPose} x={PINO_BLUME_X} y={BODEN + kp.y + huepfer(f, z.n1.ende - 10, 26, 12)} stauchY={atmen(f, 1, 0.01) * kp.stauch * hopfStauch(f, z.n1.ende + 2)} drehung={kp.dreh} blende={10} />
</Kamera>
<Blaetterwind anzahl={4} />
<Vignette />
<Untertitel zeilen={S5.liste} />
<Blende dauer={dauer} rein={12} raus={0} />
</>
);
};

View file

@ -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 (
<>
<Kamera {...kam}>
<Ort name="garten_herbst" />
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} />
{f < pflueck && <Blume x={BLUME_X} y={BLUME_Y} s={0.8} r={Math.sin(f * 0.07) * 3} />}
<Figur pose={luluPose} x={LULU_BLUME_X} y={BODEN - 6} stauchY={atmen(f, 3, 0.006)} blende={12} />
<Figur pose={noaPose} x={NOA_BLUME_X} y={BODEN + huepfer(f, hoch, 14, 10)} stauchY={atmen(f, 2) * hopfStauch(f, hoch + 10)} blende={10} />
{pinoGehtHin ? (
<Geher figur="pino" spiegeln x={pinoX} y={pinoBoden} tempo={1.4} />
) : pinoGehtZurueck ? (
<Geher figur="pino" x={pinoX} y={pinoBoden} tempo={1.4} />
) : (
<Figur pose={pinoPose} x={pinoX} y={pinoBoden + huepfer(f, z.l1.ende, 24, 12)} stauchY={atmen(f, 1, 0.01)} blende={10} />
)}
{f >= pflueck && blumeImBild && !nah && <Blume x={pinoGehtZurueck ? pinoX - 10 : blume.x} y={pinoGehtZurueck ? pinoBoden - 40 : blume.y} s={blume.s} />}
{nah && <Blume x={LULU_NASE.x - 10} y={nahBlumeY} s={0.9} r={-8} />}
</Kamera>
<Blaetterwind anzahl={4} />
<Vignette />
<Untertitel zeilen={S6.liste} />
<Blende dauer={dauer} rein={12} raus={0} />
</>
);
};

View file

@ -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 (
<>
<Kamera {...kam}>
<Ort name="garten_herbst_abend" />
<Laubhaufen x={HAUFEN_X + 220} y={BODEN + 6} />
<Figur pose="lulu_B_liegt" x={LULU_X} y={BODEN + 10} stauchY={atmen(f, 3, 0.008)} />
<Figur pose="noa_B_sitzt" x={NOA_X} y={BODEN + 10} stauchY={atmen(f, 2, 0.008)} />
<Apfel x={PINO_X + 70} y={BODEN - 6} s={0.62} r={-20} />
<Figur pose={pinoPose} x={PINO_X} y={BODEN + 4} stauchY={pinoStauch} blende={12} />
<Zzz x={PINO_X + 30} y={BODEN - 150} start={z.bild1.start} s={0.6} />
</Kamera>
<Blaetterwind anzahl={3} />
<Vignette staerke={0.45} />
<Untertitel zeilen={S7.liste} />
<Blende dauer={dauer} rein={12} raus={0} />
</>
);
};