youtube/remotion/src/folge01/lied.tsx
Lara 1db1001dfc Lulus Hals-Clip ohne Zoom, Noa klatscht im Lied, Nestbau ruhiger
- clipkorrektur.ts: Higgsfield-Kamera zoomt/schwenkt im Clip lulu_C9 (Beine
  +20 % breiter, 79 px versetzt) – je Clipbild zurückgerechnet, auch die
  Apfel-Bahn im Maul
- neue Pose noa_A_klatschen (Higgsfield, 1,5 Credits, Auftrag cbb2880c),
  Lied in Folge 1 und 2: Noa klatscht auf jeden zweiten Schlag (klatschFolge)
- schwebende Klatsch-Hände wieder entfernt
- Folge 2, Nestbau: kein seitliches Hin- und Herrutschen von Noa und Lulu
- .gitignore: .env/.env.local (ElevenLabs-Schlüssel)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MyLusGH2U5AudjoARey3N
2026-11-02 21:55:08 +01:00

96 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import {random, useCurrentFrame} from 'remotion';
import {Figur, Kamera} from '../baukasten/figur';
import {Blende, Vignette, klatschFolge} 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'],
];
// Noa klatscht im Takt (jeder zweite Schlag), bei „zusammen kriegen wir alles hin“ Arme hoch
const noaPose: [number, string][] = [
[0, 'noa_A3_lachen'],
...klatschFolge(z.r1.start, z.r4.start, TAKT),
[z.r4.start, 'noa_A_arme-hoch'],
[z.s1.start, 'noa_A3_lachen'],
...klatschFolge(z.s1.start, z.w4.start, TAKT),
[z.w4.start, 'noa_A_arme-hoch'],
];
return (
<>
<Kamera x={1000} y={690} 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={3} />
<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} />
</>
);
};