Folge 1 und 2: ruckelfreie Clips, Posen-Größen, Bilderbuch-Vögel, Klatschen im Lied, Kalender ohne Stift

- Clip-Tempi auf 1,25 (bzw. 2,5): 24-fps-Clip × 1,25 = 30 fps, jedes Videobild
  genau ein Clipbild – vorher ruckelte der Gang (Bilder doppelt/übersprungen)
- POSEN_SKALA in figur.tsx: Hocke 72 %, Sitzen 70 %, Lulu liegend 65 % (die
  Bilder sind auf Standhöhe gezeichnet); Größe geht beim Überblenden fließend mit,
  Überblendung weich an- und auslaufend → Hocke ohne Sprung
- Vögel als weiche „m“-Bögen statt Seitenansicht
- Folge 2, Szene 3: großes Blatt liegt von Anfang an (tauchte vorher plötzlich auf)
- Lied (beide Folgen): Klatsch-Fäustlinge auf jeden zweiten Schlag
- Kalender malen: Blick aufs Blatt ohne schwebende Hand/Stift
- Noas Zimmer: Noa sitzend in voller Bildgröße (Szene darauf gebaut)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MyLusGH2U5AudjoARey3N
This commit is contained in:
Lara 2026-11-02 19:14:31 +01:00
parent 4f83fbc587
commit 7de2b609ba
16 changed files with 90 additions and 56 deletions

View file

@ -19,7 +19,8 @@ export const posenStand = (frame: number, folge: Posenfolge, blende = 5) => {
let i = 0; let i = 0;
while (i + 1 < folge.length && frame >= folge[i + 1][0]) i++; while (i + 1 < folge.length && frame >= folge[i + 1][0]) i++;
const [start, pose] = folge[i]; const [start, pose] = folge[i];
const mix = i > 0 ? Math.min(1, (frame - start) / blende) : 1; const lin = i > 0 ? Math.min(1, (frame - start) / blende) : 1;
const mix = lin * lin * (3 - 2 * lin); // weich an- und auslaufen
return {pose, von: mix < 1 ? folge[i - 1][1] : undefined, mix}; return {pose, von: mix < 1 ? folge[i - 1][1] : undefined, mix};
}; };
@ -81,13 +82,24 @@ type FigurProps = Omit<Lage, 'children' | 'figur' | 'hoehe'> & {
export const NOA_HOEHE = 340; export const NOA_HOEHE = 340;
// Manche Bilder der Bibliothek sind auf dieselbe Höhe gezeichnet wie die stehende Figur,
// obwohl die Figur darin hockt, sitzt oder liegt. Hier die richtige Größe relativ zur
// Standhöhe; beim Überblenden geht die Größe fließend mit (der Kopf wandert nach unten).
export const POSEN_SKALA: Record<string, number> = {
noa_B_hocken: 0.72,
noa_B_sitzt: 0.7,
lulu_B_liegt: 0.65,
};
const skala = (pose: string) => POSEN_SKALA[pose] ?? 1;
export const Figur: React.FC<FigurProps> = ({pose, hoehe, frame, blende, ...lage}) => { export const Figur: React.FC<FigurProps> = ({pose, hoehe, frame, blende, ...lage}) => {
const jetzt = useCurrentFrame(); const jetzt = useCurrentFrame();
const folge: Posenfolge = typeof pose === 'string' ? [[0, pose]] : pose; const folge: Posenfolge = typeof pose === 'string' ? [[0, pose]] : pose;
const st = posenStand(frame ?? jetzt, folge, blende); const st = posenStand(frame ?? jetzt, folge, blende);
const figur = POSEN[st.pose].figur as FigurName; const figur = POSEN[st.pose].figur as FigurName;
const k = st.von ? skala(st.von) + (skala(st.pose) - skala(st.von)) * st.mix : skala(st.pose);
return ( return (
<Rahmen {...lage} figur={figur} hoehe={hoehe ?? NOA_HOEHE * GROESSE[figur]}> <Rahmen {...lage} figur={figur} hoehe={(hoehe ?? NOA_HOEHE * GROESSE[figur]) * k}>
{st.von && <PosenBild name={st.von} />} {st.von && <PosenBild name={st.von} />}
<PosenBild name={st.pose} deckkraft={st.mix} /> <PosenBild name={st.pose} deckkraft={st.mix} />
</Rahmen> </Rahmen>

View file

@ -69,20 +69,22 @@ export const Blaetterregen: React.FC<{start: number; x0: number; y0: number; bod
); );
}; };
// Ein Vogel von der Seite (fliegt nach rechts). `schlag` -1 … 1 = Flügel unten … oben. // Ein Vogel wie im Bilderbuch: zwei weiche Flügelbögen („m“), kleiner Körper.
// `schlag` -1 … 1 = Flügel unten … oben.
const Vogel: React.FC<{x: number; y: number; g: number; schlag: number; neigung: number}> = ({x, y, g, schlag, neigung}) => { const Vogel: React.FC<{x: number; y: number; g: number; schlag: number; neigung: number}> = ({x, y, g, schlag, neigung}) => {
const h = -8 - schlag * 24; // Flügelspitze relativ zur Schulter const h = 4 + schlag * 13; // Höhe der Flügelbögen (oben = positiv)
const fluegel = (dx: number, dy: number) => `M ${3 + dx} ${-3 + dy} Q ${-3 + dx} ${h * 0.55 + dy} ${-15 + dx} ${h + dy} Q ${-6 + dx} ${h * 0.35 + dy} ${-7 + dx} ${1 + dy} Z`; const spitze = 3 - schlag * 9; // Flügelspitzen sinken beim Abschlag
return ( return (
<g transform={`translate(${x} ${y}) rotate(${neigung}) scale(${g / 30})`}> <g transform={`translate(${x} ${y}) rotate(${neigung}) scale(${g / 30})`}>
<path d={fluegel(4, -1)} fill="#3a3540" opacity={0.85} /> <path
<path d="M -14 0 L -27 -6 L -25 0 L -27 6 Z" fill="#4a4550" /> d={`M -26 ${spitze} Q -13 ${-h - 4}, -1 0 Q 13 ${-h - 4}, 26 ${spitze}`}
<ellipse cx={0} cy={0} rx={16} ry={7} fill="#4f4a55" /> stroke="#4a4550"
<ellipse cx={1} cy={3} rx={11} ry={3.5} fill="#8a7f78" opacity={0.6} /> strokeWidth={4.5}
<circle cx={15} cy={-4} r={6.5} fill="#4f4a55" /> strokeLinecap="round"
<path d="M 20 -6 L 28 -3.5 L 20 -1.5 Z" fill="#c98a3c" /> strokeLinejoin="round"
<circle cx={17} cy={-5.5} r={1.4} fill="#f4ead8" /> fill="none"
<path d={fluegel(0, 0)} fill="#5c5662" /> />
<ellipse cx={0} cy={1.5} rx={4.5} ry={3.2} fill="#4a4550" />
</g> </g>
); );
}; };
@ -99,10 +101,10 @@ export const Voegel: React.FC<{start: number; anzahl?: number; skala?: number}>
const x = 160 + t * 5.5 - Math.abs(reihe) * 75 * skala + r('x') * 20; const x = 160 + t * 5.5 - Math.abs(reihe) * 75 * skala + r('x') * 20;
const y = 600 - t * 1.5 + reihe * 42 * skala + Math.sin(t * 0.05 + r('w') * 6) * 8; const y = 600 - t * 1.5 + reihe * 42 * skala + Math.sin(t * 0.05 + r('w') * 6) * 8;
// Flügelschlag ohne Pause, je Vogel etwas anders schnell // Flügelschlag ohne Pause, je Vogel etwas anders schnell
const takt = t * (0.34 + r('f') * 0.1) + r('p') * 6; const takt = t * (0.22 + r('f') * 0.06) + r('p') * 6;
const schlag = Math.sin(takt); const schlag = Math.sin(takt);
const g = (34 + r('g') * 8) * skala * (1 - Math.abs(reihe) * 0.03); const g = (34 + r('g') * 8) * skala * (1 - Math.abs(reihe) * 0.03);
return <Vogel key={i} x={x} y={y} g={g} schlag={schlag} neigung={-12 + Math.cos(takt) * 3} />; return <Vogel key={i} x={x} y={y} g={g} schlag={schlag} neigung={-6 + Math.cos(takt) * 2} />;
})} })}
</svg> </svg>
); );
@ -348,3 +350,31 @@ export const Laubhaufen: React.FC<{x: number; y: number; s?: number; wackeln?: n
</svg> </svg>
); );
}; };
// Klatsch-Hände fürs Lied (Noas rote Fäustlinge): klatschen auf jeden zweiten Schlag,
// damit Kinder den Rhythmus mitklatschen können. `takt` = Bilder pro Schlag.
export const Klatschen: React.FC<{x: number; y: number; takt: number; s?: number; deckkraft?: number}> = ({x, y, takt, s = 1, deckkraft = 1}) => {
const frame = useCurrentFrame();
// 0 = Hände zusammen (Klatsch auf Schlag 1, 3, 5 …), 1 = weit auseinander
const auf = Math.abs(Math.sin((Math.PI * (frame - takt)) / (2 * takt)));
const weit = 18 + auf * 52;
const klatsch = auf < 0.18;
const Faust: React.FC<{seite: 1 | -1}> = ({seite}) => (
<g transform={`translate(${seite * weit} 0) scale(${-seite} 1) rotate(${-8 + auf * 14})`}>
<path d="M -30 -34 C -46 -30, -50 6, -34 30 C -24 44, 6 46, 20 30 C 30 18, 30 -10, 20 -26 C 10 -40, -14 -42, -30 -34 Z" fill="#c8362c" />
<path d="M 14 -18 C 30 -26, 40 -12, 30 -2 C 24 4, 16 2, 12 -4 Z" fill="#c8362c" />
<rect x={-38} y={26} width={58} height={20} rx={8} fill="#e9e2d2" />
<path d="M -22 -20 C -30 -8, -28 10, -20 20" stroke="#e8786a" strokeWidth={5} strokeLinecap="round" fill="none" opacity={0.6} />
</g>
);
return (
<svg width={360} height={200} viewBox="-180 -100 360 200" style={{position: 'absolute', left: x - 180 * s, top: y - 100 * s, transform: `scale(${s})`, transformOrigin: '0 0', opacity: deckkraft, overflow: 'visible'}}>
{klatsch &&
[-60, -30, 0, 30, 60].map((w) => (
<path key={w} d="M 0 -58 L 0 -84" stroke="#e0a020" strokeWidth={6} strokeLinecap="round" transform={`rotate(${w})`} />
))}
<Faust seite={-1} />
<Faust seite={1} />
</svg>
);
};

View file

@ -501,7 +501,7 @@ export const KalenderAufTisch: React.FC<{p: number}> = ({p}) => {
export const KALENDER_TUER1 = {x: 330 + 4 * 215 + 90, y: 250 + 1 * 180 + 75}; // Türchen „1“ (Index 10) export const KALENDER_TUER1 = {x: 330 + 4 * 215 + 90, y: 250 + 1 * 180 + 75}; // Türchen „1“ (Index 10)
// Noa malt den Kalender (Blick von oben auf das Blatt, über `kalender_leer`). // Noa malt den Kalender (Blick von oben auf das Blatt, über `kalender_leer`).
// `p` 0…1: Titel, dann die Türchen Zeile für Zeile. Die Stiftspitze folgt dem Strich. // `p` 0…1: Titel, dann die Türchen Zeile für Zeile.
const drehe = (x: number, y: number) => { const drehe = (x: number, y: number) => {
const w = (-2 * Math.PI) / 180; const w = (-2 * Math.PI) / 180;
const dx = x - 960; const dx = x - 960;
@ -569,22 +569,8 @@ export const KalenderMalen: React.FC<{p: number; wackeln: number}> = ({p, wackel
})} })}
</g> </g>
</g> </g>
{/* Noas Hand mit Buntstift, von unten rechts */} {/* Aus Noas Sicht aufs Blatt: die Striche entstehen von selbst, ohne Hand und Stift
<g transform={`translate(${s.x} ${s.y}) rotate(-28)`}> (der Arm von unten wirkte wie ein schwebender Stift) */}
<path d="M -9 -4 L 0 -24 L 9 -4 Z" fill="#f1d2a6" />
<path d="M -3 -17 L 0 -24 L 3 -17 Z" fill={farbe} />
<rect x={-11} y={-6} width={22} height={260} rx={4} fill={farbe} />
<rect x={-11} y={-6} width={7} height={260} fill="rgba(255,255,255,0.18)" />
{/* Ärmel */}
<path d="M -62 150 L 66 150 L 120 900 L -150 900 Z" fill="#d39b3a" />
<path d="M -66 140 Q 2 164 70 140 L 74 176 Q 2 198 -70 176 Z" fill="#c1862c" />
{/* Handrücken und Finger, die den Stift halten */}
<ellipse cx={4} cy={118} rx={52} ry={44} fill="#f2c4a2" stroke="#dba585" strokeWidth={2} />
<ellipse cx={-30} cy={98} rx={15} ry={24} fill="#f2c4a2" stroke="#dba585" strokeWidth={2} transform="rotate(-25 -30 98)" />
<ellipse cx={-6} cy={72} rx={17} ry={13} fill="#f2c4a2" stroke="#dba585" strokeWidth={2} />
<ellipse cx={20} cy={80} rx={16} ry={12} fill="#f2c4a2" stroke="#dba585" strokeWidth={2} />
<ellipse cx={38} cy={94} rx={14} ry={11} fill="#f2c4a2" stroke="#dba585" strokeWidth={2} />
</g>
</svg> </svg>
); );
}; };

View file

@ -2,7 +2,7 @@ import React from 'react';
import {random, useCurrentFrame} from 'remotion'; import {random, useCurrentFrame} from 'remotion';
import {Figur, Kamera} from '../baukasten/figur'; import {Figur, Kamera} from '../baukasten/figur';
import {Blende, Vignette} from '../baukasten/helfer'; import {Blende, Vignette} from '../baukasten/helfer';
import {Blume, Laubhaufen} from '../baukasten/natur'; import {Blume, Klatschen, Laubhaufen} from '../baukasten/natur';
import {BODEN, Ort} from '../baukasten/orte'; import {BODEN, Ort} from '../baukasten/orte';
import {SCHRIFT, Untertitel} from '../baukasten/untertitel'; import {SCHRIFT, Untertitel} from '../baukasten/untertitel';
import {TEXTE, plane} from './plan'; import {TEXTE, plane} from './plan';
@ -85,6 +85,8 @@ export const Lied: React.FC = () => {
<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)}}> <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} ♫ ♫ {TEXTE.lied} ♫
</div> </div>
{/* Klatsch-Hände: auf jeden zweiten Schlag zum Mitklatschen */}
<Klatschen x={960} y={215} takt={TAKT} s={1.3} deckkraft={Math.min(1, f / 20)} />
<Vignette /> <Vignette />
<Untertitel zeilen={LIED.liste} /> <Untertitel zeilen={LIED.liste} />
<Blende dauer={dauer} rein={12} raus={12} /> <Blende dauer={dauer} rein={12} raus={12} />

View file

@ -148,7 +148,7 @@ export const Szene2: React.FC = () => {
{imHaufen && <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} schatten={false} />} {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} /> <Laubhaufen x={HAUFEN_X} y={BODEN + 6} wackeln={wackeln} flach={flach} />
{imHaufen ? null : noaGeht ? ( {imHaufen ? null : noaGeht ? (
<Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.5} /> <Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.25} />
) : ( ) : (
<Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung) * hockeStauch} blende={f >= hocke ? HOCKE_BLENDE : 5} /> <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung) * hockeStauch} blende={f >= hocke ? HOCKE_BLENDE : 5} />
)} )}

View file

@ -99,7 +99,7 @@ export const Szene3: React.FC = () => {
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} /> <Laubhaufen x={HAUFEN_X} y={BODEN + 6} />
{noaDa && {noaDa &&
(noaGeht ? ( (noaGeht ? (
<Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.5} /> <Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.25} />
) : ( ) : (
<Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * (1 + 0.04 * zehen)} blende={10} /> <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * (1 + 0.04 * zehen)} blende={10} />
))} ))}

View file

@ -19,7 +19,7 @@ export const LULU_APFEL_X = APFEL.x + 149;
// Den Hals senkt sie mit dem Clip lulu_C9_hals-runter; der Apfel bleibt dabei im Maul // Den Hals senkt sie mit dem Clip lulu_C9_hals-runter; der Apfel bleibt dabei im Maul
// (Maul-Bahn gemessen, luluMaulC9). Unten liegt das Maul direkt neben Pino. // (Maul-Bahn gemessen, luluMaulC9). Unten liegt das Maul direkt neben Pino.
const LULU_WARTE_X = 930; // bis zum Pflücken etwas rechts vom Apfel const LULU_WARTE_X = 930; // bis zum Pflücken etwas rechts vom Apfel
const C9_TEMPO = 1.3; const C9_TEMPO = 1.25; // 1,25 × 24 fps = 30 fps: jedes Videobild genau ein Clipbild (ruckelfrei)
const C9_START = 17; // vorher steht sie im Clip still const C9_START = 17; // vorher steht sie im Clip still
const C9_UNTEN = 105; // Kopf unten const C9_UNTEN = 105; // Kopf unten
const c9Frames = (bilder: number) => (bilder * 30) / 24 / C9_TEMPO; const c9Frames = (bilder: number) => (bilder * 30) / 24 / C9_TEMPO;
@ -140,7 +140,7 @@ export const Szene4: React.FC = () => {
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} /> <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} />} {apfelAmBaum && <Apfel x={APFEL.x} y={APFEL.y} s={0.9} r={Math.sin(f * 0.05) * 4} />}
{luluGeht ? ( {luluGeht ? (
<Geher figur="lulu" x={luluX} y={luluY} tempo={1} /> <Geher figur="lulu" x={luluX} y={luluY} tempo={1.25} />
) : imClip ? ( ) : imClip ? (
f < hoch ? ( f < hoch ? (
<ClipFigur name="lulu_C9_hals-runter" start={runter - c9Frames(C9_START)} tempo={C9_TEMPO} bis={C9_UNTEN / 24} x={luluX} y={luluY} /> <ClipFigur name="lulu_C9_hals-runter" start={runter - c9Frames(C9_START)} tempo={C9_TEMPO} bis={C9_UNTEN / 24} x={luluX} y={luluY} />

View file

@ -45,7 +45,7 @@ export const Szene5: React.FC = () => {
const {z, dauer} = S5; const {z, dauer} = S5;
// Lulu senkt den Hals (Clip) bis zur Hälfte, bleibt wackelnd stecken, hebt ihn wieder (Clip rückwärts) // Lulu senkt den Hals (Clip) bis zur Hälfte, bleibt wackelnd stecken, hebt ihn wieder (Clip rückwärts)
const C9_TEMPO = 1.1; const C9_TEMPO = 1.25; // ruckelfrei (siehe Szene 4)
const C9_START = 17; const C9_START = 17;
const C9_HALB = 52; // so weit kommt sie const C9_HALB = 52; // so weit kommt sie
const c9 = (bilder: number) => (bilder * 30) / 24 / C9_TEMPO; const c9 = (bilder: number) => (bilder * 30) / 24 / C9_TEMPO;

View file

@ -74,7 +74,7 @@ export const Szene6: React.FC = () => {
const BLENDE = 14; const BLENDE = 14;
// Hände: noa_B_hocken auf den Knien (Leinwand ca. 700, 520), noa_A_arme-hoch rechte Hand (800, 190); Fuß (675, 700), Maßstab 340/535 // Hände: noa_B_hocken auf den Knien (Leinwand ca. 700, 520), noa_A_arme-hoch rechte Hand (800, 190); Fuß (675, 700), Maßstab 340/535
const m = 340 / 535; const m = 340 / 535;
const HAND_HOCKE = {x: NOA_BLUME_X + (700 - 675) * m, y: BODEN + (520 - 700) * m}; const HAND_HOCKE = {x: NOA_BLUME_X + (700 - 675) * m * 0.72, y: BODEN + (520 - 700) * m * 0.72}; // Hocke-Bild wird auf 72 % verkleinert (POSEN_SKALA)
const HAND_OBEN = {x: NOA_BLUME_X + (800 - 675) * m, y: BODEN + (190 - 700) * m}; const HAND_OBEN = {x: NOA_BLUME_X + (800 - 675) * m, y: BODEN + (190 - 700) * m};
let blume: {x: number; y: number; s: number} = {x: BLUME_X, y: BLUME_Y, s: 0.8}; let blume: {x: number; y: number; s: number} = {x: BLUME_X, y: BLUME_Y, s: 0.8};
if (f >= pflueck) blume = {x: pfoten.x, y: pfoten.y + 20, s: 0.55}; if (f >= pflueck) blume = {x: pfoten.x, y: pfoten.y + 20, s: 0.55};
@ -148,9 +148,9 @@ export const Szene6: React.FC = () => {
<Figur pose={luluPose} x={LULU_BLUME_X} y={BODEN - 6} stauchY={atmen(f, 3, 0.006)} blende={12} /> <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, aufstehen + BLENDE, 14, 10)} stauchY={atmen(f, 2) * noaStauch * hopfStauch(f, aufstehen + BLENDE + 10)} blende={BLENDE} /> <Figur pose={noaPose} x={NOA_BLUME_X} y={BODEN + huepfer(f, aufstehen + BLENDE, 14, 10)} stauchY={atmen(f, 2) * noaStauch * hopfStauch(f, aufstehen + BLENDE + 10)} blende={BLENDE} />
{pinoGehtHin ? ( {pinoGehtHin ? (
<Geher figur="pino" spiegeln x={pinoX} y={pinoBoden} tempo={1.4} /> <Geher figur="pino" spiegeln x={pinoX} y={pinoBoden} tempo={1.25} />
) : pinoGehtZurueck ? ( ) : pinoGehtZurueck ? (
<Geher figur="pino" x={pinoX} y={pinoBoden} tempo={1.4} /> <Geher figur="pino" x={pinoX} y={pinoBoden} tempo={1.25} />
) : ( ) : (
<Figur pose={pinoPose} x={pinoX} y={pinoBoden + huepfer(f, z.l1.ende, 24, 12)} stauchY={atmen(f, 1, 0.01)} blende={10} /> <Figur pose={pinoPose} x={pinoX} y={pinoBoden + huepfer(f, z.l1.ende, 24, 12)} stauchY={atmen(f, 1, 0.01)} blende={10} />
)} )}

View file

@ -2,6 +2,7 @@ import React from 'react';
import {random, useCurrentFrame} from 'remotion'; import {random, useCurrentFrame} from 'remotion';
import {Figur, Kamera} from '../baukasten/figur'; import {Figur, Kamera} from '../baukasten/figur';
import {Blende, Vignette} from '../baukasten/helfer'; import {Blende, Vignette} from '../baukasten/helfer';
import {Klatschen} from '../baukasten/natur';
import {BODEN, Ort} from '../baukasten/orte'; import {BODEN, Ort} from '../baukasten/orte';
import {SCHRIFT, Untertitel} from '../baukasten/untertitel'; import {SCHRIFT, Untertitel} from '../baukasten/untertitel';
import {TEXTE, plane} from './plan'; import {TEXTE, plane} from './plan';
@ -58,6 +59,8 @@ export const Lied: React.FC = () => {
<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)}}> <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} ♫ ♫ {TEXTE.lied} ♫
</div> </div>
{/* Klatsch-Hände: auf jeden zweiten Schlag zum Mitklatschen */}
<Klatschen x={960} y={215} takt={TAKT} s={1.3} deckkraft={Math.min(1, f / 20)} />
<Vignette /> <Vignette />
<Untertitel zeilen={LIED.liste} /> <Untertitel zeilen={LIED.liste} />
<Blende dauer={dauer} rein={12} raus={12} /> <Blende dauer={dauer} rein={12} raus={12} />

View file

@ -125,8 +125,8 @@ export const Szene2: React.FC = () => {
<> <>
<Kamera {...kam}> <Kamera {...kam}>
<Ort name="garten" /> <Ort name="garten" />
{noaGeht ? <Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.6} /> : <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * kichStauch * (1 + 0.02 * vorbeugen)} drehung={kichDreh + 7 * vorbeugen} />} {noaGeht ? <Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.25} /> : <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * kichStauch * (1 + 0.02 * vorbeugen)} drehung={kichDreh + 7 * vorbeugen} />}
{!pinoDa ? null : pinoGeht ? <Geher figur="pino" x={pinoX} y={BODEN} tempo={1.8} /> : <Figur pose={pinoPose} x={pinoX} y={pinoY} stauchY={pinoStauchY} drehung={pinoDreh} blende={4} />} {!pinoDa ? null : pinoGeht ? <Geher figur="pino" x={pinoX} y={BODEN} tempo={2.5} /> : <Figur pose={pinoPose} x={pinoX} y={pinoY} stauchY={pinoStauchY} drehung={pinoDreh} blende={4} />}
<Zzz x={PINO_X + 40} y={BODEN - 150} start={z.p2.ende} ende={schreck} s={0.7} /> <Zzz x={PINO_X + 40} y={BODEN - 150} start={z.p2.ende} ende={schreck} s={0.7} />
</Kamera> </Kamera>
<Blaetterwind anzahl={6} /> <Blaetterwind anzahl={6} />

View file

@ -55,11 +55,11 @@ export const Szene3: React.FC = () => {
let lulu: React.ReactNode; let lulu: React.ReactNode;
const luluY = BODEN - 10; const luluY = BODEN - 10;
if (luluGeht) { if (luluGeht) {
lulu = <Geher figur="lulu" x={luluX} y={luluY} />; lulu = <Geher figur="lulu" x={luluX} y={luluY} tempo={1.25} />;
} else if (f < halsHoch) { } else if (f < halsHoch) {
lulu = <ClipFigur name="lulu_C9_hals-runter" start={halsRunter} tempo={1.25} x={LULU_X} y={luluY} />; lulu = <ClipFigur name="lulu_C9_hals-runter" start={halsRunter} tempo={1.25} x={LULU_X} y={luluY} />;
} else if (f < halsHoch + 110) { } else if (f < halsHoch + 110) {
lulu = <ClipFigur name="lulu_C9_hals-runter" start={halsHoch} tempo={1.5} rueckwaerts x={LULU_X} y={luluY} />; lulu = <ClipFigur name="lulu_C9_hals-runter" start={halsHoch} tempo={1.25} rueckwaerts x={LULU_X} y={luluY} />;
} else { } else {
lulu = ( lulu = (
<Figur <Figur
@ -82,9 +82,10 @@ export const Szene3: React.FC = () => {
[aufsBlatt, PINO_X], [aufsBlatt, PINO_X],
[aufsBlatt + 24, GROSSES_BLATT_X], [aufsBlatt + 24, GROSSES_BLATT_X],
]); ]);
// Das große Blatt segelt vom Apfelbaum herunter und landet neben Pino // Das große Blatt liegt von Anfang an neben Pino im Gras (vorher fiel es außerhalb des
const blattFaellt = aufsBlatt - 80; // Bildausschnitts und tauchte dann plötzlich unter ihm auf); Pino setzt sich darauf
const u = Math.min(1, Math.max(0, (f - blattFaellt) / 70)); const blattFaellt = -1;
const u = 1;
const schaukeln = Math.sin(u * 8) * (1 - u); const schaukeln = Math.sin(u * 8) * (1 - u);
const blattX = GROSSES_BLATT_X - 180 * (1 - u) + schaukeln * 70; const blattX = GROSSES_BLATT_X - 180 * (1 - u) + schaukeln * 70;
const blattY = BODEN - 8 - 560 * (1 - u); const blattY = BODEN - 8 - 560 * (1 - u);
@ -158,7 +159,7 @@ export const Szene3: React.FC = () => {
) : ( ) : (
<Kamera {...kam}> <Kamera {...kam}>
<Ort name="garten" /> <Ort name="garten" />
{noaGeht ? <Geher figur="noa" spiegeln x={noaX} y={BODEN} /> : <Figur pose={noaPose} x={noaX} y={BODEN} stauchY={atmen(f, 2) * hockeStauch} blende={f >= knien ? HOCKE_BLENDE : 5} />} {noaGeht ? <Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.25} /> : <Figur pose={noaPose} x={noaX} y={BODEN} stauchY={atmen(f, 2) * hockeStauch} blende={f >= knien ? HOCKE_BLENDE : 5} />}
{f > blattFaellt && <Blatt x={blattX} y={blattY} r={blattR} s={2.6} farbe="#c8642c" />} {f > blattFaellt && <Blatt x={blattX} y={blattY} r={blattR} s={2.6} farbe="#c8642c" />}
<Figur pose={pinoPose} x={pinoX} y={pinoY} stauchY={pinoStauchY} drehung={pinoDreh} blende={6} /> <Figur pose={pinoPose} x={pinoX} y={pinoY} stauchY={pinoStauchY} drehung={pinoDreh} blende={6} />
{lulu} {lulu}

View file

@ -221,7 +221,7 @@ export const Szene4: React.FC = () => {
)} )}
<Nest x={NEST_X} y={BODEN} seite="hinten" voll={f < zurueck ? 0 : nestVoll} /> <Nest x={NEST_X} y={BODEN} seite="hinten" voll={f < zurueck ? 0 : nestVoll} />
{noaGeht ? ( {noaGeht ? (
<Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.15} /> <Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.25} />
) : ( ) : (
<Figur <Figur
pose={noaPose} pose={noaPose}
@ -239,7 +239,7 @@ export const Szene4: React.FC = () => {
<Figur pose={pinoPose} x={NEST_X} y={pinoImNestY} schatten={false} /> <Figur pose={pinoPose} x={NEST_X} y={pinoImNestY} schatten={false} />
</div> </div>
) : pinoGeht ? ( ) : pinoGeht ? (
<Geher figur="pino" spiegeln={pinoSpiegel} x={pinoX} y={BODEN} tempo={1.3} /> <Geher figur="pino" spiegeln={pinoSpiegel} x={pinoX} y={BODEN} tempo={1.25} />
) : ( ) : (
<Figur <Figur
pose={pinoPose} pose={pinoPose}

View file

@ -108,7 +108,7 @@ export const Szene5: React.FC = () => {
stauchY={atmen(f, 3, 0.006)} 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} /> <ClipFigur name="lulu_C9_hals-runter" start={z.bild3.start + 10} tempo={1.25} bis={3.6} x={LULU_X5} y={BODEN - 10} />
); );
const ideeStart = z.bild4.start; const ideeStart = z.bild4.start;

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import {useCurrentFrame} from 'remotion'; import {useCurrentFrame} from 'remotion';
import {kurve} from '../bewegung'; import {kurve} from '../bewegung';
import {ClipFigur, Figur, Kamera} from '../baukasten/figur'; import {ClipFigur, Figur, Kamera, POSEN_SKALA} from '../baukasten/figur';
import {Blende, Vignette, atmen, huepfer, kamFahrt} from '../baukasten/helfer'; import {Blende, Vignette, atmen, huepfer, kamFahrt} from '../baukasten/helfer';
import {Funkeln} from '../baukasten/natur'; import {Funkeln} from '../baukasten/natur';
import {KALENDER_TUER1, KalenderAufTisch, KalenderMalen, Ort} from '../baukasten/orte'; import {KALENDER_TUER1, KalenderAufTisch, KalenderMalen, Ort} from '../baukasten/orte';
@ -143,7 +143,7 @@ export const Szene6: React.FC = () => {
]} ]}
x={NOA_X6} x={NOA_X6}
y={1000} y={1000}
hoehe={N} hoehe={N / POSEN_SKALA.noa_B_sitzt} // Szene ist auf die volle Bildgröße gebaut (Noa sitzt nah an der Kamera am Tisch)
stauchY={atmen(f, 2)} stauchY={atmen(f, 2)}
drehung={malt ? Math.sin(f * 0.3) * 0.8 : 0} drehung={malt ? Math.sin(f * 0.3) * 0.8 : 0}
schatten={false} schatten={false}
@ -151,7 +151,7 @@ export const Szene6: React.FC = () => {
<Ort name="zimmer_tisch" /> <Ort name="zimmer_tisch" />
{f >= z.malen.start && <KalenderAufTisch p={malP} />} {f >= z.malen.start && <KalenderAufTisch p={malP} />}
{imSprung ? ( {imSprung ? (
<ClipFigur name="pino_C3_freudensprung" start={sprung} tempo={1.4} x={PINO_X6} y={TISCH_Y} hoehe={N * 0.4} /> <ClipFigur name="pino_C3_freudensprung" start={sprung} tempo={1.25} x={PINO_X6} y={TISCH_Y} hoehe={N * 0.4} />
) : ( ) : (
<Figur <Figur
pose={[ pose={[

View file

@ -82,7 +82,7 @@ export const Szene7: React.FC = () => {
filter={NACHT} filter={NACHT}
/> />
) : ( ) : (
<ClipFigur name="noa_C4_winken" start={winken} tempo={1.2} x={NOA_X7} y={BODEN} filter={NACHT} /> <ClipFigur name="noa_C4_winken" start={winken} tempo={1.25} x={NOA_X7} y={BODEN} filter={NACHT} />
)} )}
<Nest x={NEST_X} y={BODEN} seite="hinten" filter={NACHT} /> <Nest x={NEST_X} y={BODEN} seite="hinten" filter={NACHT} />
<div style={{position: 'absolute', inset: 0, clipPath: `inset(0 0 ${1080 - BODEN - 4}px 0)`}}> <div style={{position: 'absolute', inset: 0, clipPath: `inset(0 0 ${1080 - BODEN - 4}px 0)`}}>