Lied 4: Noa tippt mit den Higgsfield-Posen auf die Körperteile statt Leuchtkreisen; Karte zeigt die Pose klein
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1a6e76e346
commit
a705b32b1e
2 changed files with 36 additions and 37 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import {AbsoluteFill, Audio, Sequence, interpolate, staticFile, useCurrentFrame} from 'remotion';
|
||||
import {INTRO_OPTIONEN, Intro, OUTRO, Outro} from '../../baukasten/buch';
|
||||
import {Figur, Kamera} from '../../baukasten/figur';
|
||||
import {Figur, Kamera, type Posenfolge} from '../../baukasten/figur';
|
||||
import {Blende, Vignette, kamFahrt} from '../../baukasten/helfer';
|
||||
import {BODEN, Ort} from '../../baukasten/orte';
|
||||
import {SCHRIFT} from '../../baukasten/untertitel';
|
||||
|
|
@ -21,7 +21,8 @@ export type Bild = {
|
|||
kamera?: {x: number; y: number; z: number};
|
||||
leuchten?: (Punkt & {farbe: string; r?: number; ab?: number})[];
|
||||
karte?: React.ReactNode;
|
||||
noa?: string;
|
||||
noa?: string | Posenfolge; // Posenfolge mit Frames ab Liedbeginn (z. B. Pose je Wort)
|
||||
tipps?: number[]; // Sekunden, an denen Noa kurz im Takt nachfedert (Tippen)
|
||||
pino?: string;
|
||||
lulu?: string;
|
||||
luluStreckt?: boolean;
|
||||
|
|
@ -152,7 +153,7 @@ const Garten: React.FC<{daten: LiedDaten; szene: (i: number, z: Zeile) => Bild}>
|
|||
<Kamera {...kam}>
|
||||
<Ort name="garten" />
|
||||
<Figur pose={b.lulu ?? 'lulu_A3_lachen'} x={PLATZ.lulu} y={BODEN} drehung={schwank(0.5, 2)} stauchY={strecken} blende={8} />
|
||||
<Figur pose={b.noa ?? 'noa_A3_lachen'} x={PLATZ.noa} y={BODEN + wipp(0.3, 10)} drehung={schwank(0, 2.5)} blende={6} />
|
||||
<Figur pose={b.noa ?? 'noa_A3_lachen'} x={PLATZ.noa} y={BODEN + wipp(0.3, 10)} drehung={schwank(0, 2.5)} stauchY={1 - (b.tipps ?? []).reduce((m, tp) => m + 0.03 * Math.max(0, 1 - Math.abs(f - s(tp)) / 4), 0)} blende={4} />
|
||||
<Figur pose={b.pino ?? 'pino_A3_lachen'} x={PLATZ.pino} y={BODEN + wipp(0, 14)} drehung={schwank(1, 3)} blende={8} />
|
||||
{bilder.map((bb, k) =>
|
||||
(bb.leuchten ?? []).map((l, j) => <Leuchtkreis key={`${k}-${j}`} {...l} ab={s(l.ab ?? zeilen[k].start - 0.1)} bis={zeilenEnde(k)} />),
|
||||
|
|
|
|||
|
|
@ -1,29 +1,14 @@
|
|||
import React from 'react';
|
||||
import {Figur} from '../../baukasten/figur';
|
||||
import {punkt} from '../../baukasten/helfer';
|
||||
import {BODEN} from '../../baukasten/orte';
|
||||
import {Figur, type Posenfolge} from '../../baukasten/figur';
|
||||
import {SPRACHE} from '../../baukasten/sprache';
|
||||
import {type Bild, EinfachesLied, type LiedDaten, PLATZ, type Punkt, type Zeile, wortZeit} from '../einfach/EinfachesLied';
|
||||
import {type Bild, EinfachesLied, type LiedDaten, PLATZ, type Zeile, wortZeit} from '../einfach/EinfachesLied';
|
||||
import {DE, ES, FR} from '../einfach/daten/lied04';
|
||||
|
||||
// Liedvideo „Wer hat den längsten Hals?“ (Pinos Liederbuch, Lied 4: Körperteile) in DE/ES/FR.
|
||||
// Refrain: Lulu streckt den Hals. Strophen (Ohren, Hände, Beine, Arme): nah bei Noa, die Stellen leuchten,
|
||||
// die Karte zeigt eine kleine Noa mit der leuchtenden Stelle. Turbo-Teil (3×): jedes genannte Körperteil
|
||||
// leuchtet zu seinem Wort, im dritten Durchgang langsam. Drehbuch drehbuecher/lieder/lied04-koerperteile/.
|
||||
// Refrain: Lulu streckt den Hals, Noa zeigt auf ihn. Strophen (Ohren, Hände, Beine, Arme): nah bei Noa, sie tippt
|
||||
// auf das Körperteil (Higgsfield-Posen noa_B_tippt-*), die Karte zeigt die Pose klein. Turbo-Teil (3×): zu jedem
|
||||
// gesungenen Körperteil die passende Pose, im dritten Durchgang langsam. Keine Leuchtkreise (Nutzerin 09.11.). Drehbuch drehbuecher/lieder/lied04-koerperteile/.
|
||||
|
||||
// Körperteile in noa_A3_lachen (Leinwand = Bild + 339/156, gemessen)
|
||||
const TEIL: Record<string, [number, number][]> = {
|
||||
ohren: [[607, 306], [809, 306]],
|
||||
haende: [[629, 576], [809, 551]],
|
||||
beine: [[684, 616], [744, 616]],
|
||||
arme: [[629, 476], [799, 466]],
|
||||
nase: [[734, 296]],
|
||||
bauch: [[709, 486]],
|
||||
hals: [[709, 361]],
|
||||
fuesse: [[684, 691], [754, 691]],
|
||||
zeh: [[759, 721]],
|
||||
};
|
||||
const FARBE = '#d9482b'; // kräftiges Rot-Orange: hebt sich von Jacke und Gesicht ab
|
||||
const WOERTER: [RegExp, string][] = [
|
||||
[/^(ohren|orejas|oreilles)/i, 'ohren'],
|
||||
[/^(h[aä]nde|manos|mains)/i, 'haende'],
|
||||
|
|
@ -36,16 +21,25 @@ const WOERTER: [RegExp, string][] = [
|
|||
[/^(zeh|deditos|orteils)/i, 'zeh'],
|
||||
];
|
||||
const teilVon = (wort: string) => WOERTER.find(([re]) => re.test(wort.replace(/[^\p{L}]/gu, '')))?.[1];
|
||||
const anNoa = (teil: string): Punkt[] => TEIL[teil].map(([cx, cy]) => punkt('noa', PLATZ.noa, BODEN, cx, cy));
|
||||
|
||||
// Karte: kleine Noa, die genannte Stelle leuchtet
|
||||
// Higgsfield-Posen (09.11.2026): Noa tippt wirklich auf das Körperteil. Hände = klatschen, Hals = sie zeigt
|
||||
// auf Lulus langen Hals, Zeh = wie Füße.
|
||||
const POSE: Record<string, string> = {
|
||||
ohren: 'noa_B_tippt-ohren',
|
||||
nase: 'noa_B_tippt-nase',
|
||||
bauch: 'noa_B_tippt-bauch',
|
||||
beine: 'noa_B_tippt-beine',
|
||||
arme: 'noa_B_tippt-arme',
|
||||
fuesse: 'noa_B_tippt-fuesse',
|
||||
zeh: 'noa_B_tippt-fuesse',
|
||||
haende: 'noa_A_klatschen',
|
||||
hals: 'noa_B_zeigen',
|
||||
};
|
||||
|
||||
// Karte: kleine Noa in derselben Pose (Bild statt Text)
|
||||
const NoaKarte: React.FC<{teil: string}> = ({teil}) => (
|
||||
<div style={{position: 'relative', width: 204, height: 204}}>
|
||||
<Figur pose="noa_A3_lachen" x={102} y={198} hoehe={180} schatten={false} />
|
||||
{TEIL[teil].map(([cx, cy], k) => {
|
||||
const p = punkt('noa', 102, 198, cx, cy, 180);
|
||||
return <div key={k} style={{position: 'absolute', left: p.x - 16, top: p.y - 16, width: 32, height: 32, borderRadius: '50%', border: `5px solid ${FARBE}`, background: `${FARBE}66`}} />;
|
||||
})}
|
||||
<Figur pose={POSE[teil]} x={102} y={198} hoehe={180} schatten={false} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
@ -64,28 +58,32 @@ const lage = (i: number) => {
|
|||
return i - k;
|
||||
};
|
||||
const NOA_NAH = {x: PLATZ.noa, y: 765, z: 1.75}; // ganze Noa über dem Mitlesetext
|
||||
const LULU_HALS = punkt('lulu', PLATZ.lulu, BODEN, 322 + 320, 23 + 230);
|
||||
|
||||
const istTipp = (w: string) => /^(tipp|toc)/i.test(w.replace(/[^\p{L}]/gu, ''));
|
||||
|
||||
const szene = (i: number, z: Zeile): Bild => {
|
||||
const a = z.abschnitt.toLowerCase();
|
||||
const n = lage(i);
|
||||
const woerter = z.zeile.split(' ').map((w) => w.split('@')[0]);
|
||||
const tipps = woerter.flatMap((w, k) => (istTipp(w) ? [wortZeit(z, k)] : []));
|
||||
if (a.startsWith('chorus')) {
|
||||
if (n === 0) return {kamera: {x: PLATZ.lulu - 60, y: 420, z: 1.5}, luluStreckt: true, lulu: 'lulu_A3_lachen'};
|
||||
if (n === 1) return {leuchten: [{...LULU_HALS, farbe: FARBE, r: 90}]};
|
||||
return {};
|
||||
if (n === 1) return {noa: 'noa_B_zeigen', luluStreckt: true, tipps};
|
||||
return {tipps};
|
||||
}
|
||||
if (a.startsWith('verse')) {
|
||||
const teil = woerter.map(teilVon).find(Boolean) ?? 'ohren';
|
||||
if (n === 0) return {kamera: NOA_NAH, noa: i % 2 ? 'noa_A5_blick-links' : 'noa_A6_blick-rechts', ruhig: true, karte: <NoaKarte teil={teil} />};
|
||||
return {kamera: NOA_NAH, leuchten: anNoa(teil).map((p) => ({...p, farbe: FARBE, r: 46})), karte: <NoaKarte teil={teil} />};
|
||||
return {kamera: NOA_NAH, noa: POSE[teil], tipps, karte: <NoaKarte teil={teil} />};
|
||||
}
|
||||
if (a.startsWith('bridge')) {
|
||||
const leuchten = woerter.flatMap((w, k) => {
|
||||
// zu jedem gesungenen Körperteil die passende Pose (knapp vor dem Wort, damit sie beim Wort steht)
|
||||
const folge: Posenfolge = [[0, 'noa_A3_lachen']];
|
||||
woerter.forEach((w, k) => {
|
||||
const teil = teilVon(w);
|
||||
return teil ? anNoa(teil).map((p) => ({...p, farbe: FARBE, r: 40, ab: wortZeit(z, k) - 0.05})) : [];
|
||||
if (teil) folge.push([Math.round(wortZeit(z, k) * 30) - 4, POSE[teil]]);
|
||||
});
|
||||
return {kamera: {x: PLATZ.noa - 40, y: 720, z: 1.5}, leuchten, ruhig: a.includes('slow')};
|
||||
return {kamera: {x: PLATZ.noa - 40, y: 720, z: 1.5}, noa: folge.length > 1 ? folge : 'noa_A3_lachen', luluStreckt: woerter.some((w) => teilVon(w) === 'hals'), ruhig: a.includes('slow')};
|
||||
}
|
||||
return {ruhig: true};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue