import React from 'react'; import {useCurrentFrame} from 'remotion'; import {kurve} from '../bewegung'; import {Figur, Geher, Kamera} from '../baukasten/figur'; import {Blende, LULU_MAUL_A1, LULU_MAUL_LINKS, Vignette, atmen, hopfStauch, huepfer, kamFahrt, punkt, ruck} from '../baukasten/helfer'; import {Blaetterregen, Blatt, LAUB, Nest, Ruf} from '../baukasten/natur'; import {BODEN, NEST_X, Ort} from '../baukasten/orte'; import {NOA_X, PINO_X} from './szene2'; import {GROSSES_BLATT_X} from './szene3'; import {W} from '../baukasten/sprache'; import {Untertitel} from '../baukasten/untertitel'; import {plane} from './plan'; // Szene 4: Ein Bett aus Blättern. Zählen bis 5, Lulu niest, das Nest entsteht. const NOA_ZAEHL_X = 1170; const PINO_ZAEHL_X = 1790; const LULU_BAUM_X = 650; const REIHE = [0, 1, 2, 3, 4].map((i) => ({x: 1290 + i * 78, y: BODEN + 4})); export const S4 = plane('s4', [ {id: 'n1', wer: 'NOA'}, {id: 'e1', wer: 'ERZ'}, {id: 'e2', wer: 'ERZ'}, {id: 'e3', wer: 'ERZ'}, {id: 'n2', wer: 'NOA', pause: 0.6}, {id: 'c1', wer: 'NOA', dauer: 1.1, pause: 0.5}, {id: 'c2', wer: 'NOA', dauer: 1.1, pause: 0.5}, {id: 'c3', wer: 'NOA', dauer: 1.1, pause: 0.5}, {id: 'c4', wer: 'NOA', dauer: 1.1, pause: 0.5}, {id: 'c5', wer: 'NOA', dauer: 1.4}, {id: 'bild1', dauer: 2.6}, {id: 'l1', wer: 'LULU'}, {id: 'bild2', dauer: 2.4}, {id: 'bild3', dauer: 2.6}, {id: 'l2', wer: 'LULU'}, {id: 'n3', wer: 'NOA'}, {id: 'bild4', dauer: 4.6}, {id: 'bild5', dauer: 2.2}, {id: 'e4', wer: 'ERZ'}, {id: 'e5', wer: 'ERZ'}, ]); export const Szene4: React.FC = () => { const f = useCurrentFrame(); const {z, dauer} = S4; // Abschnitte (Schnitte) const zumStapel = z.e1.start; // Noa und Pino gehen zum Holzstapel const amBaum = z.bild1.start; // Schnitt zu Lulu am Apfelbaum const niesen = z.bild3.start; // Hatschi! const zurueck = z.bild4.start; // Schnitt zum Nestbau const zaehlen = [z.c1, z.c2, z.c3, z.c4, z.c5].map((c) => c.start); // Noa const noaGehtVon = zumStapel + 20; const noaGehtBis = noaGehtVon + 60; const noaX = kurve(f, [ [noaGehtVon, NOA_X + 140], [noaGehtBis, NOA_ZAEHL_X], ]); const noaGeht = f > noaGehtVon && f < noaGehtBis; const noaStehPose: [number, string][] = [ [0, 'noa_A3_lachen'], [noaGehtBis, 'noa_A6_blick-rechts'], [z.c5.ende, 'noa_A3_lachen'], [z.bild3.start + 4, 'noa_B2_erschrocken'], [z.l2.ende, 'noa_A3_lachen'], [z.e4.start, 'noa_A3_lachen'], ]; // Hocke: Abschnitte, in denen Noa hockt; Wechsel als weiche Überblendung der Standbilder // (der Hinsetz-Clip ist anders gezeichnet), dabei kurz absinken bzw. sich strecken const HOCKE_BLENDE = 14; const hockZeiten: [number, number][] = [ [-1000, z.n1.start + 30], [z.n2.start - 15, z.c5.ende], [zurueck, z.e4.start], ]; const inHocke = hockZeiten.some(([a, b]) => f >= a && f < b); const stehPose = (t: number) => noaStehPose.filter(([s]) => s <= t).pop()![1]; const noaPose: [number, string][] = [...noaStehPose, ...hockZeiten.flatMap(([a, b]): [number, string][] => [[a, 'noa_B_hocken'], [b, stehPose(b)]])].sort((p, q) => p[0] - q[0]); const bump = (t: number) => (f >= t && f < t + HOCKE_BLENDE ? Math.sin(((f - t) / HOCKE_BLENDE) * Math.PI) : 0); const hockeStauch = hockZeiten.reduce((s, [a, b]) => s * (1 - 0.05 * bump(a)) * (1 + 0.03 * bump(b)), 1); // beim Hinlegen jedes Blatts kurz nach vorn beugen const beugen = zaehlen.reduce((s, c) => s + Math.max(0, Math.sin(Math.min(1, Math.max(0, (f - c + 8) / 16)) * Math.PI)), 0); // beim Nestbau: früher seitliches Hin- und Herrutschen (wirkte unruhig) – jetzt ruhig, nur die Blätter bewegen sich // Pino const pinoGehtVon = zumStapel + 40; const pinoGehtBis = pinoGehtVon + 110; const pinoX = kurve(f, [ [pinoGehtVon, PINO_X + 50], [pinoGehtBis, PINO_ZAEHL_X], [zurueck + 10, PINO_ZAEHL_X], [zurueck + 60, NEST_X + 150], [z.bild5.start - 20, NEST_X + 150], [z.bild5.start, NEST_X], ]); const insNest = huepfer(f, z.bild5.start - 20, 46, 20); // Sprung ins Nest // Anfang wie Ende von Szene 3: Pino lehnt am großen Blatt und richtet sich auf const sitzt = kurve(f, [ [z.n1.start + 10, 1], [z.n1.start + 34, 0], ]); const pinoGeht = (f > pinoGehtVon && f < pinoGehtBis) || (f > zurueck + 10 && f < zurueck + 60); const pinoSpiegel = f > pinoGehtVon && f < pinoGehtBis; // nach rechts const imNest = f >= z.bild5.start; // Pino sitzt beim Zählen rechts von Noa: seine Bilder schauen nach rechts, darum gespiegelt (schaut zu ihr) const pinoZuNoa = f >= pinoGehtBis && f <= zurueck + 10; const pinoPose: [number, string][] = [ [0, 'pino_A1_augen-zu'], [z.n1.start + 20, 'pino_A2_augen-halb'], [pinoGehtBis, 'pino_A6_blick-rechts'], ...zaehlen.map((c) => [c, 'pino_A4_staunen'] as [number, string]), [z.c5.ende, 'pino_A3_lachen'], [niesen + 4, 'pino_B2_erschrocken'], [z.n3.start + 20, 'pino_A3_lachen'], [z.bild4.ende - 30, 'pino_A2_augen-halb'], [z.bild5.start, 'pino_A6_blick-rechts'], [z.e4.start, 'pino_A3_lachen'], [z.e5.start + 60, 'pino_A2_augen-halb'], ]; const zaehlHuepfer = zaehlen.reduce((s, c) => s + huepfer(f, c + 4, 10, 10), 0); const nestVoll = kurve(f, [ [zurueck + 20, 0.15], [z.bild4.ende, 1], ]); const pinoImNestY = BODEN + 2 - huepfer(f, z.bild5.start, -10, 20); // Lulu am Baum const strecken = kurve(f, [ [amBaum + 10, 0], [amBaum + 50, 1], ]); const kitzeln = z.bild2.start; const zurueckLehnen = kurve(f, [ [kitzeln + 10, 0], [niesen - 4, 1], [niesen, -0.6], [niesen + 20, 0], ]); const luluPose: [number, string][] = [ [0, 'lulu_A5_blick-links'], [kitzeln + 10, 'lulu_A1_augen-zu'], [niesen + 18, 'lulu_B2_erschrocken'], [z.n3.start + 20, 'lulu_A3_lachen'], ]; const luluY = BODEN - 10 - strecken * 26; const luluStauch = atmen(f, 3, 0.006) * (1 + 0.02 * strecken); const luluDreh = zurueckLehnen * 4; // Punkt im Bild von Lulu, mit Drehung und Stauchung wie die Figur selbst const luluPunkt = (c: {cx: number; cy: number}) => { const p = punkt('lulu', 0, 0, c.cx, c.cy); const dy = p.y * luluStauch; const a = (luluDreh * Math.PI) / 180; return {x: LULU_BAUM_X + p.x * Math.cos(a) - dy * Math.sin(a), y: luluY + p.x * Math.sin(a) + dy * Math.cos(a)}; }; // Maul wandert mit dem Posenwechsel A5 → A1 mit const m5 = luluPunkt(LULU_MAUL_LINKS); const m1 = luluPunkt(LULU_MAUL_A1); const zuA1 = kurve(f, [ [kitzeln + 10, 0], [kitzeln + 15, 1], ]); const maul = {x: m5.x + (m1.x - m5.x) * zuA1, y: m5.y + (m1.y - m5.y) * zuA1}; // Blätter hängen am Ast, Lulu zupft eins nach dem anderen ab; das Bündel im Maul wächst mit const astMaul = punkt('lulu', LULU_BAUM_X, BODEN - 36, LULU_MAUL_LINKS.cx, LULU_MAUL_LINKS.cy); const zupfen = [0, 1, 2, 3, 4].map((i) => { const ast = {x: astMaul.x - 150 + i * 55, y: astMaul.y - 115 - (i % 2) * 35}; const los = amBaum + 50 + i * 18; const t = Math.min(1, Math.max(0, (f - los) / 20)); const e = t * t * (3 - 2 * t); return { x: ast.x + (maul.x - ast.x) * e, y: ast.y + (maul.y - ast.y) * e - Math.sin(t * Math.PI) * 30, r: t > 0 ? 160 + t * 380 : 160 + Math.sin(f * 0.12 + i * 2) * 12, deck: Math.min(1, Math.max(0, (f - amBaum - i * 3) / 12)), angekommen: t >= 1, }; }); const imMaul = f < niesen ? zupfen.filter((b) => b.angekommen).length : 0; // Kamera const kam = kamFahrt(f, [ [0, 950, 790, 2.0], [z.n1.ende, 1000, 760, 1.6], [z.e1.start + 60, 1380, 720, 1.35], [z.e3.ende, 1450, 780, 1.6], [z.n2.start, 1480, 820, 1.85], [z.c5.ende, 1480, 820, 1.85], [amBaum, 470, 400, 1.85], [z.l1.ende, 500, 380, 1.9], [niesen - 5, 560, 370, 2.0], [niesen + 12, 960, 540, 1.0], [z.n3.ende, 960, 560, 1.05], [zurueck, 1480, 800, 1.7], [z.bild5.start, 1480, 830, 2.4], [z.e5.start, 1400, 760, 1.5], [dauer, 1350, 740, 1.4], ]); kam.y += ruck(f, niesen, 9, 22); const lulu = ; const luluBeimNest = f >= zurueck && ; return ( <> {f >= amBaum && f < zurueck && lulu} {f >= amBaum && f < niesen && zupfen.map((b, i) => (!b.angekommen ? : null))} {LAUB.slice(0, imMaul).map((c, i) => ( ))} {/* Reihe aus fünf Blättern vor dem Holzstapel */} {f < zurueck + 40 && REIHE.map((p, i) => f > zaehlen[i] - 6 ? zurueck ? 1 - (f - zurueck) / 40 : 1} /> : null, )} {noaGeht ? ( ) : ( )} {/* das große Blatt aus Szene 3 bleibt liegen */} {f < zurueck + 40 && zurueck ? 1 - (f - zurueck) / 40 : 1} />} {imNest ? (
) : pinoGeht ? ( ) : ( )} {luluBeimNest}
{/* Zahlen beim Zählen */} {zaehlen.map((c, i) => ( ))} ); };