- src/lieder/lied01: Äpfel fliegen bei jeder Zahl in den Korb, Zahl + Finger oben rechts, Äpfel leuchten beim Zählen, Lulu holt sich den obersten Apfel (Clip C9), „nur noch …?“ → 9 - Wortzeiten je Sprache mit faster-whisper gemessen (Zeilenanfang: Wortende − 0,38 s, an Lied 3 geeicht) - Komposition „Lied01“, Sprache über --props wie bei den Folgen; Gesprochenes noch ohne Aufnahmen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
406 lines
16 KiB
TypeScript
406 lines
16 KiB
TypeScript
import React from 'react';
|
||
import {AbsoluteFill, Audio, Sequence, interpolate, spring, staticFile, useCurrentFrame} from 'remotion';
|
||
import {INTRO_OPTIONEN, Intro, OUTRO, Outro} from '../../baukasten/buch';
|
||
import {ClipFigur, Figur, Kamera, type Posenfolge} from '../../baukasten/figur';
|
||
import {Blende, LULU_KOPF, Vignette, huepfer, kamFahrt, luluMaulC9, punkt} from '../../baukasten/helfer';
|
||
import {Apfel, Funkeln, Ruf} from '../../baukasten/natur';
|
||
import {BODEN, Ort} from '../../baukasten/orte';
|
||
import {SCHRIFT} from '../../baukasten/untertitel';
|
||
import {plane} from '../../baukasten/zeit';
|
||
import {Karaoke} from '../karaoke';
|
||
import {Noten} from '../lied03/Lied03';
|
||
import {DAUER, ECHO, LIED, T, ZAHLEN, ZEILEN} from './zeiten';
|
||
|
||
// Liedvideo „Eins, zwei – Pino ist dabei!“ (Pinos Liederbuch, Lied 1, Zählen bis 10) – ENTWURF.
|
||
// Ton je Sprache: Suno-Fassungen auf −14 LUFS in public/musik/lieder/lied01-<sprache>.mp3 (siehe zeiten.ts).
|
||
// Sprache wie bei den Folgen: npx remotion render … Lied01 --props='{"sprache":"es"}'.
|
||
// Bei jeder Zahl fliegt ein Apfel vom alten Apfelbaum in Pinos Korb; oben rechts stehen die Zahl
|
||
// und die Finger (Daumen = 1, wie in Deutschland üblich; ab 6 ist die erste Hand voll).
|
||
// Im Instrumental holt Lulu sich den obersten Apfel: „Jetzt sind es nur noch …?“ – „Neun!“
|
||
// Gesprochenes (ERZ-Ansage, Lulus Frage, Noas Frage, „Mampf!“) fehlt noch; im Entwurf nur als Schrift.
|
||
|
||
const FPS = 30;
|
||
const s = (sek: number) => Math.round(sek * FPS);
|
||
const SCHLAG = (FPS * 60) / 102; // gemessen am Refrain: ein Takt ≈ 2,35 s
|
||
const ANFANG = s(T.c1);
|
||
|
||
const PINO_X = 600;
|
||
const KORB_X = 790;
|
||
const RAND_Y = BODEN - 118; // Oberkante des Korbs
|
||
const NOA_X = 1430;
|
||
// Lulu senkt den Hals (Clip lulu_C9_hals-runter) bis Clipbild 75: dann ist das Maul
|
||
// 302 px links von ihren Füßen genau auf Höhe des obersten Apfels im Korb.
|
||
const C9_TEMPO = 1.25;
|
||
const C9_START = 17;
|
||
const C9_TIEF = 75;
|
||
const c9 = (bilder: number) => (bilder * FPS) / 24 / C9_TEMPO;
|
||
const LULU_X = KORB_X + 302;
|
||
const LULU_Y = BODEN - 6;
|
||
|
||
// Plätze der zehn Äpfel im Korb (relativ zur Korbmitte am Rand): unten 5, darüber 4, oben 1
|
||
const PLAETZE: [number, number][] = [
|
||
[-76, -10], [-38, -13], [0, -14], [38, -13], [76, -10],
|
||
[-57, -42], [-19, -45], [19, -45], [57, -42],
|
||
[0, -76],
|
||
];
|
||
// Wo die Äpfel vorher im alten Apfelbaum hängen
|
||
const AM_BAUM: [number, number][] = [
|
||
[470, 300], [300, 340], [610, 390], [180, 420], [420, 430],
|
||
[540, 480], [250, 500], [680, 520], [360, 545], [130, 560],
|
||
];
|
||
const FLUG = 20; // Frames vom Baum in den Korb
|
||
|
||
// Zeitplan des Gags
|
||
const G = s(T.gag);
|
||
const RUNTER = G + 24;
|
||
const UNTEN = RUNTER + c9(C9_TIEF - C9_START);
|
||
const HOCH = UNTEN + 20;
|
||
const OBEN = HOCH + c9(C9_TIEF);
|
||
const MAMPF = OBEN + 8;
|
||
|
||
const imAbschnitt = (f: number, von: number, bis: number) => f >= s(von) && f < s(bis);
|
||
|
||
const kamera = (f: number) => {
|
||
const P: [number, number, number, number][] = [
|
||
[T.c1, 960, 650, 1.08],
|
||
[T.v1 - 0.6, 830, 640, 1.2], // Baum, Korb, Pino
|
||
[T.c2, 960, 650, 1.08],
|
||
[T.v2 - 0.6, 830, 640, 1.2],
|
||
[T.echo, 900, 690, 1.28], // Äpfel im Korb leuchten
|
||
[T.gag, 960, 620, 1.1], // Lulu ganz im Bild
|
||
[T.frage, 900, 670, 1.22],
|
||
[T.c3, 960, 650, 1.08],
|
||
[T.ende, 960, 630, 1.04],
|
||
];
|
||
const p: [number, number, number, number][] = [];
|
||
P.forEach(([t, x, y, z], i) => {
|
||
if (i > 0) p.push([s(t) - 21, ...P[i - 1].slice(1)] as [number, number, number, number]);
|
||
p.push([s(t), x, y, z]);
|
||
});
|
||
return kamFahrt(f, p);
|
||
};
|
||
|
||
// Noa klatscht im Refrain auf jeden zweiten Schlag
|
||
const klatschen = (von: number, bis: number): Posenfolge => {
|
||
const folge: Posenfolge = [];
|
||
for (let t = ANFANG; t < s(bis); t += 2 * SCHLAG) {
|
||
if (t < s(von)) continue;
|
||
const k = Math.round(t);
|
||
folge.push([k - 3, 'noa_A_klatschen'], [k + 4, 'noa_A3_lachen']);
|
||
}
|
||
return folge;
|
||
};
|
||
const zeigen = (zeiten: number[]): Posenfolge => zeiten.flatMap((t) => [[s(t) - 2, 'noa_B_zeigen'], [s(t) + 12, 'noa_A3_lachen']] as Posenfolge);
|
||
|
||
const NOA: Posenfolge = [
|
||
[0, 'noa_A3_lachen'],
|
||
...klatschen(T.c1, T.c1 + 9.2),
|
||
...zeigen(ZAHLEN.slice(0, 5)),
|
||
...klatschen(T.c2, T.c2 + 9.2),
|
||
...zeigen(ZAHLEN.slice(5)),
|
||
[s(T.echo) - 4, 'noa_B_zeigen'],
|
||
[s(T.echoEnde + 0.4), 'noa_A3_lachen'],
|
||
[UNTEN, 'noa_A5_blick-links'],
|
||
[MAMPF, 'noa_A4_staunen'],
|
||
[s(T.frage), 'noa_B_zeigen'],
|
||
[s(T.antwort), 'noa_A_arme-hoch'],
|
||
[s(T.antwort + 1.4), 'noa_A3_lachen'],
|
||
...klatschen(T.c3, T.c3 + 9.2),
|
||
[s(T.ende), 'noa_A_arm-hoch-winken'],
|
||
];
|
||
|
||
const PINO: Posenfolge = [
|
||
[0, 'pino_A3_lachen'],
|
||
[UNTEN - 10, 'pino_A4_staunen'],
|
||
[MAMPF + 20, 'pino_A3_lachen'],
|
||
[s(T.antwort), 'pino_A_hinterbeine'],
|
||
[s(T.antwort + 1.6), 'pino_A3_lachen'],
|
||
];
|
||
|
||
const LULU: Posenfolge = [
|
||
[0, 'lulu_A3_lachen'],
|
||
[G, 'lulu_A5_blick-links'], // „Darf ich einen Apfel?“
|
||
[OBEN, 'lulu_A_apfel'],
|
||
[MAMPF + 14, 'lulu_A1_augen-zu'],
|
||
[MAMPF + 24, 'lulu_A3_lachen'],
|
||
];
|
||
|
||
// Korb: Rückwand (vor den Äpfeln) und Vorderseite (danach)
|
||
const KorbHinten: React.FC = () => (
|
||
<svg width={260} height={60} viewBox="-130 -30 260 60" style={{position: 'absolute', left: KORB_X - 130, top: RAND_Y - 30, overflow: 'visible'}}>
|
||
<ellipse cx={0} cy={0} rx={112} ry={24} fill="#5a3a22" />
|
||
</svg>
|
||
);
|
||
const KorbVorne: React.FC = () => (
|
||
<svg width={260} height={160} viewBox="-130 -30 260 160" style={{position: 'absolute', left: KORB_X - 130, top: RAND_Y - 30, overflow: 'visible'}}>
|
||
<path d="M -112 0 C -108 60, -96 104, -78 116 L 78 116 C 96 104, 108 60, 112 0 C 80 18, -80 18, -112 0 Z" fill="#c08a4e" />
|
||
{[20, 46, 72, 98].map((y) => (
|
||
<path key={y} d={`M ${-110 + y * 0.28} ${y} C -40 ${y + 14}, 40 ${y + 14}, ${110 - y * 0.28} ${y}`} stroke="#9a6a38" strokeWidth={7} fill="none" />
|
||
))}
|
||
{[-80, -48, -16, 16, 48, 80].map((x) => (
|
||
<path key={x} d={`M ${x} 16 L ${x * 0.82} 114`} stroke="#a8763f" strokeWidth={5} opacity={0.7} />
|
||
))}
|
||
<path d="M -112 0 C -80 22, 80 22, 112 0" stroke="#8a5a2e" strokeWidth={12} fill="none" strokeLinecap="round" />
|
||
<path d="M -112 0 C -80 -24, 80 -24, 112 0" stroke="#8a5a2e" strokeWidth={8} fill="none" strokeLinecap="round" opacity={0.9} />
|
||
</svg>
|
||
);
|
||
|
||
// Ein Apfel des Liedes: hängt im Baum, fliegt bei seiner Zahl in den Korb, leuchtet beim Zählen
|
||
const Liedapfel: React.FC<{i: number; f: number}> = ({i, f}) => {
|
||
// Apfel 10 holt sich Lulu: vom Korb ins Maul, mit dem Hals wieder hoch, dann ist er im Bild lulu_A_apfel
|
||
if (i === 9 && f >= UNTEN && f < OBEN) {
|
||
const bild = f < HOCH ? C9_TIEF : C9_TIEF - ((f - HOCH) * C9_TEMPO * 24) / FPS;
|
||
const m = luluMaulC9(bild);
|
||
const maul = punkt('lulu', LULU_X, LULU_Y, m.cx, m.cy);
|
||
const p = interpolate(f, [UNTEN, UNTEN + 6], [0, 1], {extrapolateRight: 'clamp'});
|
||
const x = interpolate(p, [0, 1], [KORB_X + PLAETZE[9][0], maul.x]);
|
||
const y = interpolate(p, [0, 1], [RAND_Y + PLAETZE[9][1], maul.y + 6]);
|
||
return <Apfel x={x} y={y} s={0.82} />;
|
||
}
|
||
if (i === 9 && f >= OBEN) return null;
|
||
const start = s(ZAHLEN[i]) - 6;
|
||
const [bx, by] = AM_BAUM[i];
|
||
const zx = KORB_X + PLAETZE[i][0];
|
||
const zy = RAND_Y + PLAETZE[i][1];
|
||
if (f < start) return <Apfel x={bx} y={by + Math.sin(f * 0.06 + i) * 3} s={0.82} r={Math.sin(f * 0.06 + i) * 5} />;
|
||
const p = Math.min(1, (f - start) / FLUG);
|
||
const x = interpolate(p, [0, 1], [bx, zx]);
|
||
const y = interpolate(p, [0, 1], [by, zy]) - Math.sin(p * Math.PI) * 160;
|
||
// Beim Zählen nach Strophe 2 leuchtet der Apfel kurz auf
|
||
const leuchten = interpolate(f, [s(ECHO[i]), s(ECHO[i]) + 4, s(ECHO[i]) + 18], [0, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||
const landen = p >= 1 ? 1 + 0.12 * Math.sin(Math.min(1, (f - start - FLUG) / 8) * Math.PI) : 1;
|
||
return (
|
||
<>
|
||
{leuchten > 0 && (
|
||
<div style={{position: 'absolute', left: zx - 50, top: zy - 50, width: 100, height: 100, borderRadius: '50%', background: 'radial-gradient(circle, rgba(255,230,140,0.95), rgba(255,230,140,0))', opacity: leuchten}} />
|
||
)}
|
||
<Apfel x={x} y={y} s={0.82 * landen * (1 + 0.18 * leuchten)} r={p < 1 ? p * 360 : 0} />
|
||
</>
|
||
);
|
||
};
|
||
|
||
// Hand mit hochgestreckten Fingern (Daumen zählt als 1)
|
||
const HAUT = '#f4cfa6';
|
||
const KONTUR = '#b9825a';
|
||
const FINGER: {x: number; b: number; l: number; dreh: number}[] = [
|
||
{x: 22, b: 24, l: 62, dreh: -48}, // Daumen
|
||
{x: 48, b: 22, l: 84, dreh: -6},
|
||
{x: 74, b: 22, l: 92, dreh: 0},
|
||
{x: 100, b: 22, l: 84, dreh: 5},
|
||
{x: 124, b: 20, l: 66, dreh: 12},
|
||
];
|
||
const Hand: React.FC<{finger: number; neu: number; spiegeln?: boolean}> = ({finger, neu, spiegeln = false}) => (
|
||
<svg width={170} height={230} viewBox="0 0 160 220" style={{transform: spiegeln ? 'scaleX(-1)' : undefined, overflow: 'visible'}}>
|
||
{FINGER.map((fi, k) => {
|
||
const hoch = k < finger;
|
||
// der zuletzt dazugekommene Finger schnellt hoch
|
||
const l = hoch ? (k === finger - 1 ? 18 + (fi.l - 18) * neu : fi.l) : 18;
|
||
const fussY = k === 0 ? 150 : 112;
|
||
return (
|
||
<rect
|
||
key={k}
|
||
x={fi.x - fi.b / 2 + 6}
|
||
y={fussY - l}
|
||
width={fi.b}
|
||
height={l + 20}
|
||
rx={fi.b / 2}
|
||
fill={HAUT}
|
||
stroke={KONTUR}
|
||
strokeWidth={4}
|
||
transform={`rotate(${fi.dreh} ${fi.x + 6} ${fussY + 10})`}
|
||
/>
|
||
);
|
||
})}
|
||
<rect x={30} y={100} width={112} height={100} rx={40} fill={HAUT} stroke={KONTUR} strokeWidth={4} />
|
||
{/* eingeklappte Finger als kleine Knöchel */}
|
||
{FINGER.slice(1).map((fi, k) => (k + 1 >= finger ? <path key={k} d={`M ${fi.x - 4} 118 q 11 10 22 0`} stroke={KONTUR} strokeWidth={3} fill="none" opacity={0.7} /> : null))}
|
||
</svg>
|
||
);
|
||
|
||
// Zahl und Hände oben rechts
|
||
type Anzeige = {n: number | '?'; seit: number; deck: number};
|
||
const anzeige = (f: number): Anzeige | null => {
|
||
const zaehle = (zeiten: number[], von: number, bis: number, basis = 0) => {
|
||
if (!imAbschnitt(f, von, bis)) return null;
|
||
const k = zeiten.filter((t) => f >= s(t)).length;
|
||
const seit = k > 0 ? f - s(zeiten[k - 1]) : 0;
|
||
const deck = Math.min(interpolate(f, [s(von), s(von) + 8], [0, 1], {extrapolateRight: 'clamp'}), interpolate(f, [s(bis) - 10, s(bis)], [1, 0], {extrapolateLeft: 'clamp'}));
|
||
return {n: basis + k, seit, deck};
|
||
};
|
||
return (
|
||
zaehle(ZAHLEN.slice(0, 5), ZAHLEN[0] - 0.2, T.c2 - 0.3) ??
|
||
zaehle(ZAHLEN.slice(5), ZAHLEN[5] - 0.2, T.echo - 0.1, 5) ??
|
||
zaehle(ECHO, ECHO[0] - 0.1, T.echoEnde + 1) ??
|
||
(imAbschnitt(f, T.frage, T.c3 - 0.5)
|
||
? {
|
||
n: f >= s(T.antwort) ? 9 : '?',
|
||
seit: f >= s(T.antwort) ? f - s(T.antwort) : f - s(T.frage),
|
||
deck: Math.min(interpolate(f, [s(T.frage), s(T.frage) + 8], [0, 1], {extrapolateRight: 'clamp'}), interpolate(f, [s(T.c3 - 0.5) - 10, s(T.c3 - 0.5)], [1, 0], {extrapolateLeft: 'clamp'})),
|
||
}
|
||
: null)
|
||
);
|
||
};
|
||
|
||
const Zahltafel: React.FC = () => {
|
||
const f = useCurrentFrame();
|
||
const a = anzeige(f);
|
||
if (!a || a.n === 0) return null;
|
||
const pop = spring({frame: a.seit, fps: FPS, config: {damping: 9, stiffness: 160}});
|
||
const neu = interpolate(a.seit, [0, 8], [0, 1], {extrapolateRight: 'clamp'});
|
||
let inhalt: React.ReactNode;
|
||
if (a.n === '?') {
|
||
// drei Punkte zählen herunter bis zur Antwort
|
||
const frei = s(T.antwort) - s(T.frage + 1.2);
|
||
const punkte = f < s(T.frage + 1.2) ? 3 : Math.max(1, 3 - Math.floor(((f - s(T.frage + 1.2)) / frei) * 3));
|
||
inhalt = <div style={{fontSize: 150, color: '#b8a68a', width: 330, textAlign: 'center'}}>{f < s(T.frage + 1.2) ? '?' : '•'.repeat(punkte)}</div>;
|
||
} else {
|
||
const n = a.n;
|
||
inhalt = (
|
||
<>
|
||
<div style={{fontSize: 190, lineHeight: 1, color: '#c8502c', minWidth: 150, textAlign: 'center', transform: `scale(${0.6 + 0.4 * pop})`}}>{n}</div>
|
||
<div style={{display: 'flex', gap: 4, alignItems: 'flex-end'}}>
|
||
<Hand finger={Math.min(n, 5)} neu={n <= 5 ? neu : 1} />
|
||
{n > 5 && <Hand finger={n - 5} neu={neu} spiegeln />}
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
return (
|
||
<div
|
||
style={{
|
||
position: 'absolute',
|
||
top: 44,
|
||
right: 56,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 18,
|
||
padding: '18px 34px',
|
||
borderRadius: 36,
|
||
background: 'rgba(251,244,228,0.94)',
|
||
border: '8px solid #3f5a8a',
|
||
boxShadow: '0 8px 26px rgba(60,40,20,0.22)',
|
||
fontFamily: SCHRIFT,
|
||
fontWeight: 600,
|
||
opacity: a.deck,
|
||
transform: `scale(${0.88 + 0.12 * a.deck})`,
|
||
transformOrigin: '100% 0',
|
||
}}
|
||
>
|
||
{inhalt}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const Garten: React.FC = () => {
|
||
const f = useCurrentFrame();
|
||
const kam = kamera(f);
|
||
const ph = (f - ANFANG) / SCHLAG;
|
||
const schlag = Math.abs(Math.sin(ph * Math.PI));
|
||
const wipp = (phase: number, h: number) => -Math.abs(Math.sin(ph * Math.PI + phase)) * h;
|
||
const schwank = (phase: number, g: number) => Math.sin((ph * Math.PI) / 2 + phase) * g;
|
||
const ruhig = f >= s(T.c3) ? 0.6 : 1;
|
||
const singt = f >= ANFANG && f < s(T.ende + 1);
|
||
|
||
const pinoHuepf = [...ZAHLEN, ...ECHO].reduce((a, t) => a + huepfer(f, s(t) + 14, 24, 11), 0) + huepfer(f, s(T.antwort), 40, 14);
|
||
const luluImClip = f >= RUNTER && f < OBEN;
|
||
const abend = interpolate(f, [s(T.c3 - 1), s(T.c3 + 2)], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
|
||
const chor = [T.c1, T.c2, T.c3].some((c) => imAbschnitt(f, c, c + 9.6));
|
||
const kopf = punkt('lulu', LULU_X, LULU_Y, LULU_KOPF.cx, LULU_KOPF.cy);
|
||
|
||
return (
|
||
<>
|
||
<Kamera {...kam}>
|
||
<Ort name="garten_herbst" />
|
||
<div style={{position: 'absolute', inset: 0, opacity: abend}}>
|
||
<Ort name="garten_herbst_abend" />
|
||
</div>
|
||
<KorbHinten />
|
||
{luluImClip ? (
|
||
f < HOCH ? (
|
||
<ClipFigur name="lulu_C9_hals-runter" start={RUNTER - c9(C9_START)} tempo={C9_TEMPO} bis={C9_TIEF / 24} x={LULU_X} y={LULU_Y} ersatz="lulu_A_hals-unten" />
|
||
) : (
|
||
<ClipFigur name="lulu_C9_hals-runter" start={HOCH} tempo={C9_TEMPO} bis={C9_TIEF / 24} rueckwaerts x={LULU_X} y={LULU_Y} ersatz="lulu_A_hals-unten" />
|
||
)
|
||
) : (
|
||
<Figur pose={LULU} x={LULU_X} y={LULU_Y} drehung={singt ? schwank(0.5, 2 * ruhig) : 0} stauchY={1 - 0.012 * schlag} blende={6} />
|
||
)}
|
||
{new Array(10).fill(0).map((_, i) => (
|
||
<Liedapfel key={i} i={i} f={f} />
|
||
))}
|
||
<KorbVorne />
|
||
<Figur pose={NOA} x={NOA_X} y={BODEN + (singt ? wipp(0.3, 10 * ruhig) : 0)} drehung={singt ? schwank(0, 3 * ruhig) : 0} blende={3} />
|
||
<Figur pose={PINO} x={PINO_X} y={BODEN + (singt ? wipp(0, 12 * ruhig) : 0) - pinoHuepf} drehung={singt ? schwank(1, 4 * ruhig) : 0} stauchY={1 - 0.03 * schlag} blende={6} />
|
||
<Ruf text={LIED.mampf} x={kopf.x - 140} y={kopf.y - 40} start={MAMPF} dauer={30} groesse={80} farbe="#c8362c" />
|
||
<Funkeln x={kopf.x - 60} y={kopf.y + 20} start={s(T.antwort + 0.6)} dauer={50} s={1.2} />
|
||
</Kamera>
|
||
{chor && <Noten />}
|
||
<Zahltafel />
|
||
{/* Liedtitel im ersten Refrain */}
|
||
<div
|
||
style={{
|
||
position: 'absolute',
|
||
top: 46,
|
||
left: 0,
|
||
right: 0,
|
||
textAlign: 'center',
|
||
fontFamily: SCHRIFT,
|
||
fontWeight: 600,
|
||
fontSize: 44,
|
||
color: '#8a6a4a',
|
||
opacity: interpolate(f, [s(T.c1), s(T.c1 + 0.6), s(T.v1 - 1.2), s(T.v1 - 0.4)], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}),
|
||
}}
|
||
>
|
||
♫ {LIED.titel} ♫
|
||
</div>
|
||
{/* Lulus Frage im Instrumental, solange die Aufnahme fehlt */}
|
||
<div
|
||
style={{
|
||
position: 'absolute',
|
||
top: 50,
|
||
left: 60,
|
||
maxWidth: 1560,
|
||
padding: '12px 30px 16px',
|
||
borderRadius: 26,
|
||
background: 'rgba(251,244,228,0.92)',
|
||
boxShadow: '0 6px 24px rgba(60,40,20,0.18)',
|
||
fontFamily: SCHRIFT,
|
||
fontWeight: 600,
|
||
fontSize: 46,
|
||
color: '#4f7a3a',
|
||
opacity: interpolate(f, [G, G + 8, RUNTER + 40, RUNTER + 50], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}),
|
||
}}
|
||
>
|
||
{LIED.bitte}
|
||
</div>
|
||
<Vignette />
|
||
</>
|
||
);
|
||
};
|
||
|
||
// Intro (Buch geht auf) bis kurz vor das erste gesungene Wort
|
||
const INTRO_DAUER = s(T.c1 - 0.2);
|
||
const INTRO = plane([{id: 'buch', dauer: 1.5}, {id: 't1', dauer: 1.2}, {id: 't2', dauer: 0.5}], {...INTRO_OPTIONEN, vorlauf: 0.2, nachlauf: 0.4});
|
||
const OUTRO_START = DAUER - OUTRO.dauer;
|
||
|
||
export const Lied01: React.FC = () => (
|
||
<AbsoluteFill style={{background: '#f4ead8'}}>
|
||
<Sequence durationInFrames={OUTRO_START + 12}>
|
||
<Garten />
|
||
</Sequence>
|
||
<Sequence from={INTRO_DAUER} durationInFrames={OUTRO_START - INTRO_DAUER + 12}>
|
||
<Blende dauer={OUTRO_START - INTRO_DAUER + 12} rein={10} raus={12} />
|
||
</Sequence>
|
||
<Sequence durationInFrames={INTRO_DAUER}>
|
||
<AbsoluteFill style={{background: '#f4ead8'}}>
|
||
<Intro plan={INTRO} titel={LIED.buchTitel} posen={['pino_A3_lachen', 'noa_A3_lachen', 'lulu_A3_lachen']} />
|
||
</AbsoluteFill>
|
||
</Sequence>
|
||
<Sequence from={OUTRO_START}>
|
||
<Outro posen={['pino_A3_lachen', 'noa_A_arm-hoch-winken', 'lulu_A3_lachen']} />
|
||
</Sequence>
|
||
<Karaoke zeilen={ZEILEN} />
|
||
<Audio src={staticFile(LIED.datei)} />
|
||
</AbsoluteFill>
|
||
);
|
||
|
||
export const LIED01_DAUER = DAUER;
|