- Texte stehen nur noch in remotion/src/folge02/texte/<sprache>.json, die Szenen enthalten nur Ablauf und Bewegung - Sprache beim Rendern wählbar (render.mjs --sprache es, Studio über --props); Zeitplan richtet sich nach Textlänge bzw. Aufnahmen der Sprache - Baukasten-Wörter je Sprache in baukasten/sprache.ts: Kanalname im Buch (mit festem Umbruch für ES/FR), „Heute: …“, Sprechernamen, Kalendertitel, „Hatschi!“; Kalenderbild für ES/FR vorberechnet - Stimmen je Sprache: stimmen/<sprache>.json, public/stimmen/folge02/<sprache>/, stimmen.mts --sprache, eigene Stimmen je Sprache möglich - neues werkzeuge/texte.mts: prüft fehlende Übersetzungen, --tabelle zum Gegenlesen - README, CLAUDE.md und Drehbuch-Hinweise angepasst Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
import React from 'react';
|
|
import {useCurrentFrame} from 'remotion';
|
|
import {kurve} from '../bewegung';
|
|
import {ClipFigur, Figur, Kamera} from '../baukasten/figur';
|
|
import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt, ruck} from '../baukasten/helfer';
|
|
import {Funkeln, Nest} from '../baukasten/natur';
|
|
import {BODEN, NEST_X, Ort} from '../baukasten/orte';
|
|
import {SCHRIFT, Untertitel} from '../baukasten/untertitel';
|
|
import {plane} from './plan';
|
|
|
|
// Szene 5: Pino will nicht schlafen. Noa hat eine Idee.
|
|
|
|
const NOA_X5 = 1150;
|
|
const PINO_RAUS_X = 1370;
|
|
const LULU_X5 = 1790;
|
|
|
|
export const S5 = plane('s5', [
|
|
{id: 'bild1', dauer: 3.0},
|
|
{id: 'bild2', dauer: 2.2},
|
|
{id: 'p1', wer: 'PINO'},
|
|
{id: 'l1', wer: 'LULU'},
|
|
{id: 'bild3', dauer: 3.0},
|
|
{id: 'e1', wer: 'ERZ'},
|
|
{id: 'e2', wer: 'ERZ', pause: 3},
|
|
{id: 'bild4', dauer: 1.2},
|
|
{id: 'n1', wer: 'NOA'},
|
|
]);
|
|
|
|
const Denkblase: React.FC<{x: number; y: number; start: number; ende: number}> = ({x, y, start, ende}) => {
|
|
const f = useCurrentFrame();
|
|
if (f < start || f > ende) return null;
|
|
const auf = Math.min(1, (f - start) / 12) * Math.min(1, (ende - f) / 8);
|
|
const punkte = Math.floor((f - start) / 14) % 4;
|
|
return (
|
|
<div style={{position: 'absolute', left: x, top: y, opacity: auf, transform: `scale(${0.6 + 0.4 * auf})`}}>
|
|
<div style={{position: 'absolute', left: -14, top: 70, width: 16, height: 16, borderRadius: '50%', background: '#fff8ea'}} />
|
|
<div style={{position: 'absolute', left: 4, top: 40, width: 26, height: 26, borderRadius: '50%', background: '#fff8ea'}} />
|
|
<div
|
|
style={{
|
|
position: 'absolute',
|
|
left: 20,
|
|
top: -60,
|
|
width: 140,
|
|
height: 90,
|
|
borderRadius: '50%',
|
|
background: '#fff8ea',
|
|
boxShadow: '0 4px 14px rgba(60,40,20,0.2)',
|
|
fontFamily: SCHRIFT,
|
|
fontSize: 60,
|
|
fontWeight: 600,
|
|
color: '#8a6a4a',
|
|
textAlign: 'center',
|
|
lineHeight: '70px',
|
|
}}
|
|
>
|
|
{'.'.repeat(punkte)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const Szene5: React.FC = () => {
|
|
const f = useCurrentFrame();
|
|
const {z, dauer} = S5;
|
|
|
|
const aufwachen = z.bild2.start;
|
|
const raus = aufwachen + 22;
|
|
const imNest = f < raus;
|
|
const stampfen = [z.p1.start + 2, z.p1.start + 40, z.p1.start + 78];
|
|
|
|
const pinoNestPose: [number, string][] = [
|
|
[0, 'pino_A2_augen-halb'],
|
|
[20, 'pino_A4_staunen'],
|
|
[60, 'pino_A2_augen-halb'],
|
|
[z.bild1.ende - 25, 'pino_A1_augen-zu'],
|
|
[aufwachen, 'pino_A4_staunen'],
|
|
];
|
|
const pinoPose: [number, string][] = [
|
|
[0, 'pino_A_hinterbeine'],
|
|
[z.l1.start + 10, 'pino_A5_blick-links'],
|
|
[z.bild3.start + 10, 'pino_B_blick-nach-oben'],
|
|
[z.bild4.start + 10, 'pino_A4_staunen'],
|
|
];
|
|
const pinoNestY = kurve(f, [
|
|
[0, BODEN + 12],
|
|
[25, BODEN + 2],
|
|
[z.bild1.ende - 20, BODEN + 10],
|
|
[aufwachen, BODEN + 6],
|
|
[aufwachen + 6, BODEN - 6],
|
|
]);
|
|
const pinoX = kurve(f, [
|
|
[raus, NEST_X - 40],
|
|
[raus + 20, PINO_RAUS_X],
|
|
]);
|
|
const stampfY = stampfen.reduce((s, t) => s + huepfer(f, t, 22, 9), 0) + huepfer(f, raus, 40, 16);
|
|
const stampfS = stampfen.reduce((s, t) => s * hopfStauch(f, t, 9), 1) * hopfStauch(f, raus, 16);
|
|
|
|
const lulu =
|
|
f < z.bild3.start + 10 ? (
|
|
<Figur
|
|
pose={[
|
|
[0, 'lulu_A6_blick-rechts'],
|
|
[z.bild2.start + 10, 'lulu_A5_blick-links'],
|
|
[z.l1.start, 'lulu_A4_staunen'],
|
|
]}
|
|
x={LULU_X5}
|
|
y={BODEN - 10}
|
|
stauchY={atmen(f, 3, 0.006)}
|
|
/>
|
|
) : (
|
|
<ClipFigur name="lulu_C9_hals-runter" start={z.bild3.start + 10} tempo={1.4} bis={3.6} x={LULU_X5} y={BODEN - 10} />
|
|
);
|
|
|
|
const ideeStart = z.bild4.start;
|
|
const noaPose: [number, string][] = [
|
|
[0, 'noa_A6_blick-rechts'],
|
|
[aufwachen + 10, 'noa_A4_staunen'],
|
|
[z.l1.start, 'noa_A6_blick-rechts'],
|
|
[z.bild3.start, 'noa_A5_blick-links'],
|
|
[z.bild3.start + 40, 'noa_A6_blick-rechts'],
|
|
[z.e1.start + 30, 'noa_A5_blick-links'],
|
|
[z.e2.start, 'noa_A6_blick-rechts'],
|
|
[ideeStart, 'noa_A_arm-hoch-winken'],
|
|
[z.n1.start + 4, 'noa_A3_lachen'],
|
|
];
|
|
const noaKopfNeigen = f > z.bild3.start && f < ideeStart ? Math.sin((f - z.bild3.start) * 0.05) * 2.5 : 0;
|
|
|
|
const kam = kamFahrt(f, [
|
|
[0, NEST_X, 860, 2.6],
|
|
[aufwachen, NEST_X, 860, 2.6],
|
|
[raus + 20, 1380, 820, 2.0],
|
|
[z.p1.ende, 1380, 800, 1.9],
|
|
[z.l1.start + 10, 1300, 620, 1.3],
|
|
[z.bild3.start + 40, 1300, 660, 1.35],
|
|
[z.e2.ende, 1280, 760, 1.6],
|
|
[ideeStart + 10, NOA_X5 + 40, 760, 1.9],
|
|
[dauer, NOA_X5 + 40, 760, 2.0],
|
|
]);
|
|
kam.x += stampfen.reduce((s, t) => s + ruck(f, t, 3, 10), 0);
|
|
|
|
return (
|
|
<>
|
|
<Kamera {...kam}>
|
|
<Ort name="garten" />
|
|
<Figur pose={noaPose} x={NOA_X5} y={BODEN + huepfer(f, ideeStart, 26, 14)} stauchY={atmen(f, 2) * hopfStauch(f, ideeStart, 14)} drehung={noaKopfNeigen} />
|
|
<Nest x={NEST_X} y={BODEN} seite="hinten" />
|
|
{imNest && (
|
|
<div style={{position: 'absolute', inset: 0, clipPath: `inset(0 0 ${1080 - BODEN - 4}px 0)`}}>
|
|
<Figur pose={pinoNestPose} x={NEST_X} y={pinoNestY} schatten={false} stauchY={atmen(f, 1, 0.012)} />
|
|
</div>
|
|
)}
|
|
<Nest x={NEST_X} y={BODEN} seite="vorne" />
|
|
{!imNest && <Figur pose={pinoPose} x={pinoX} y={BODEN + stampfY} stauchY={stampfS * atmen(f, 1)} />}
|
|
{lulu}
|
|
<Denkblase x={NOA_X5 + 70} y={BODEN - 420} start={z.bild3.start + 5} ende={ideeStart} />
|
|
<Funkeln x={NOA_X5 + 20} y={BODEN - 330} start={ideeStart + 2} dauer={36} s={1.1} />
|
|
</Kamera>
|
|
<Vignette />
|
|
<Untertitel zeilen={S5.liste} />
|
|
<Blende dauer={dauer} rein={0} raus={12} />
|
|
</>
|
|
);
|
|
};
|