youtube/remotion/src/folge02/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

69 lines
2.8 KiB
TypeScript

import React from 'react';
import {random, useCurrentFrame} from 'remotion';
import {Figur, Kamera} from '../baukasten/figur';
import {Blende, Vignette, klatschFolge} from '../baukasten/helfer';
import {BODEN, Ort} from '../baukasten/orte';
import {SCHRIFT, Untertitel} from '../baukasten/untertitel';
import {TEXTE, plane} from './plan';
// Refrain „Einer klein und einer groß“ (aus Folge 1), einmal. Die drei 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},
],
{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;
return (
<>
<Kamera x={980} y={690} zoom={1.3}>
<Ort name="garten" />
<Figur pose="lulu_A3_lachen" x={1240} y={BODEN} drehung={schwank(0.5, 2.2)} stauchY={1 - 0.012 * schlag} />
<Figur
pose={[
[0, 'noa_A3_lachen'],
// Noa klatscht im Takt (jeder zweite Schlag)
...klatschFolge(z.r1.start, z.r4.start, TAKT),
[z.r4.start, 'noa_A_arme-hoch'],
]}
x={960}
y={BODEN + wipp(0.3, 10)}
drehung={schwank(0, 3)}
blende={3}
/>
<Figur pose="pino_A3_lachen" x={720} y={BODEN + wipp(0, 16)} drehung={schwank(1, 4)} stauchY={1 - 0.03 * schlag} />
</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} />
</>
);
};