Folge 2 überarbeitet nach den Lehren aus Folge 1
- Noas Hocke (Szenen 3 und 4) als weiche Überblendung der Standbilder statt Hinsetz-Clip (anders gezeichnet); Beugen/Schieben am Nest bleiben - Noas Schritttempo passend zur Geschwindigkeit (Szene 2: 1.6, Szene 4 zügiger) - Pino schaut beim Zählen zu Noa (gespiegelt) - leeres Nest: kein Schatten ohne Blätter (doppelter Schatten entfernt) - Lied (Folge 1 und 2): Kamera etwas tiefer, Figuren höher im Bild Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MyLusGH2U5AudjoARey3N
This commit is contained in:
parent
083e878b97
commit
4f83fbc587
6 changed files with 39 additions and 28 deletions
|
|
@ -174,7 +174,7 @@ export const NEST_BILD = {b: 440, h: 260, x: 220, y: 180}; // Bildgröße und La
|
||||||
export const NestBlaetter: React.FC<{voll?: number; seite: 'hinten' | 'vorne'}> = ({voll = 1, seite}) => (
|
export const NestBlaetter: React.FC<{voll?: number; seite: 'hinten' | 'vorne'}> = ({voll = 1, seite}) => (
|
||||||
<>
|
<>
|
||||||
{seite === 'hinten' && (
|
{seite === 'hinten' && (
|
||||||
<div style={{position: 'absolute', left: -170, top: -30, width: 340, height: 60, borderRadius: '50%', background: 'radial-gradient(closest-side, rgba(70,45,25,0.45), rgba(70,45,25,0))'}} />
|
<div style={{position: 'absolute', left: -170, top: -30, width: 340, height: 60, borderRadius: '50%', background: 'radial-gradient(closest-side, rgba(70,45,25,0.45), rgba(70,45,25,0))', opacity: Math.min(1, voll * 3)}} />
|
||||||
)}
|
)}
|
||||||
{NEST[seite].map((b, i) =>
|
{NEST[seite].map((b, i) =>
|
||||||
b.reihe < voll ? <Blatt key={i} x={b.x * (0.5 + 0.5 * voll)} y={b.y * (0.4 + 0.6 * voll)} r={b.r} s={b.s} farbe={b.farbe} /> : null,
|
b.reihe < voll ? <Blatt key={i} x={b.x * (0.5 + 0.5 * voll)} y={b.y * (0.4 + 0.6 * voll)} r={b.r} s={b.s} farbe={b.farbe} /> : null,
|
||||||
|
|
@ -190,9 +190,6 @@ export const Nest: React.FC<{x: number; y: number; voll?: number; seite: 'hinten
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{position: 'absolute', left: x, top: y, transform: `scale(${s})`, transformOrigin: '0 0', filter}}>
|
<div style={{position: 'absolute', left: x, top: y, transform: `scale(${s})`, transformOrigin: '0 0', filter}}>
|
||||||
{seite === 'hinten' && (
|
|
||||||
<div style={{position: 'absolute', left: -170, top: -30, width: 340, height: 60, borderRadius: '50%', background: 'radial-gradient(closest-side, rgba(70,45,25,0.45), rgba(70,45,25,0))'}} />
|
|
||||||
)}
|
|
||||||
<NestBlaetter voll={voll} seite={seite} />
|
<NestBlaetter voll={voll} seite={seite} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export const Lied: React.FC = () => {
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Kamera x={1000} y={600} zoom={1.3}>
|
<Kamera x={1000} y={690} zoom={1.3}>
|
||||||
<Ort name="garten_herbst" />
|
<Ort name="garten_herbst" />
|
||||||
<Laubhaufen x={HAUFEN_X + 380} y={BODEN + 6} />
|
<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={luluPose} x={1300} y={BODEN} drehung={schwank(0.5, 2.2)} stauchY={1 - 0.012 * schlag} blende={8} />
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export const Lied: React.FC = () => {
|
||||||
const schwank = (phase: number, g: number) => Math.sin((f * Math.PI) / (2 * TAKT) + phase) * g;
|
const schwank = (phase: number, g: number) => Math.sin((f * Math.PI) / (2 * TAKT) + phase) * g;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Kamera x={980} y={600} zoom={1.3}>
|
<Kamera x={980} y={690} zoom={1.3}>
|
||||||
<Ort name="garten" />
|
<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="lulu_A3_lachen" x={1240} y={BODEN} drehung={schwank(0.5, 2.2)} stauchY={1 - 0.012 * schlag} />
|
||||||
<Figur
|
<Figur
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ export const Szene2: 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={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.6} /> : <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={1.8} /> : <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>
|
||||||
|
|
|
||||||
|
|
@ -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, Geher, Kamera, NoaHocke} from '../baukasten/figur';
|
import {ClipFigur, Figur, Geher, Kamera} from '../baukasten/figur';
|
||||||
import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer';
|
import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer';
|
||||||
import {Blaetterwind, Blatt, Voegel} from '../baukasten/natur';
|
import {Blaetterwind, Blatt, Voegel} from '../baukasten/natur';
|
||||||
import {BODEN, Ort} from '../baukasten/orte';
|
import {BODEN, Ort} from '../baukasten/orte';
|
||||||
|
|
@ -125,7 +125,11 @@ export const Szene3: React.FC = () => {
|
||||||
[z.l1.start + 15, 'noa_A3_lachen'],
|
[z.l1.start + 15, 'noa_A3_lachen'],
|
||||||
[z.e1.start, 'noa_A6_blick-rechts'],
|
[z.e1.start, 'noa_A6_blick-rechts'],
|
||||||
[z.bild2.start + 30, 'noa_B1_traurig'],
|
[z.bild2.start + 30, 'noa_B1_traurig'],
|
||||||
|
[knien, 'noa_B_hocken'],
|
||||||
];
|
];
|
||||||
|
// in die Hocke: Standbilder weich überblenden, dabei kurz absinken
|
||||||
|
const HOCKE_BLENDE = 14;
|
||||||
|
const hockeStauch = f >= knien && f < knien + HOCKE_BLENDE ? 1 - 0.05 * Math.sin(((f - knien) / HOCKE_BLENDE) * Math.PI) : 1;
|
||||||
const noaGeht = f >= knien - 36 && f < knien;
|
const noaGeht = f >= knien - 36 && f < knien;
|
||||||
|
|
||||||
const kam = kamFahrt(f, [
|
const kam = kamFahrt(f, [
|
||||||
|
|
@ -154,7 +158,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} /> : f >= knien ? <NoaHocke start={knien} x={noaX} y={BODEN} /> : <Figur pose={noaPose} x={noaX} y={BODEN} stauchY={atmen(f, 2)} />}
|
{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} />}
|
||||||
{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}
|
||||||
|
|
|
||||||
|
|
@ -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 {Figur, Geher, Kamera, NOA_HOCKE_FRAMES, NoaHocke} from '../baukasten/figur';
|
import {Figur, Geher, Kamera} from '../baukasten/figur';
|
||||||
import {Blende, LULU_MAUL_A1, LULU_MAUL_LINKS, Vignette, atmen, hopfStauch, huepfer, kamFahrt, punkt, ruck} from '../baukasten/helfer';
|
import {Blende, LULU_MAUL_A1, LULU_MAUL_LINKS, Vignette, atmen, hopfStauch, huepfer, kamFahrt, punkt, ruck} from '../baukasten/helfer';
|
||||||
import {Blaetterregen, Blatt, LAUB, Nest, Ruf} from '../baukasten/natur';
|
import {Blaetterregen, Blatt, LAUB, Nest, Ruf} from '../baukasten/natur';
|
||||||
import {BODEN, NEST_X, Ort} from '../baukasten/orte';
|
import {BODEN, NEST_X, Ort} from '../baukasten/orte';
|
||||||
|
|
@ -54,13 +54,13 @@ export const Szene4: React.FC = () => {
|
||||||
|
|
||||||
// Noa
|
// Noa
|
||||||
const noaGehtVon = zumStapel + 20;
|
const noaGehtVon = zumStapel + 20;
|
||||||
const noaGehtBis = noaGehtVon + 90;
|
const noaGehtBis = noaGehtVon + 60;
|
||||||
const noaX = kurve(f, [
|
const noaX = kurve(f, [
|
||||||
[noaGehtVon, NOA_X + 140],
|
[noaGehtVon, NOA_X + 140],
|
||||||
[noaGehtBis, NOA_ZAEHL_X],
|
[noaGehtBis, NOA_ZAEHL_X],
|
||||||
]);
|
]);
|
||||||
const noaGeht = f > noaGehtVon && f < noaGehtBis;
|
const noaGeht = f > noaGehtVon && f < noaGehtBis;
|
||||||
const noaPose: [number, string][] = [
|
const noaStehPose: [number, string][] = [
|
||||||
[0, 'noa_A3_lachen'],
|
[0, 'noa_A3_lachen'],
|
||||||
[noaGehtBis, 'noa_A6_blick-rechts'],
|
[noaGehtBis, 'noa_A6_blick-rechts'],
|
||||||
[z.c5.ende, 'noa_A3_lachen'],
|
[z.c5.ende, 'noa_A3_lachen'],
|
||||||
|
|
@ -68,17 +68,19 @@ export const Szene4: React.FC = () => {
|
||||||
[z.l2.ende, 'noa_A3_lachen'],
|
[z.l2.ende, 'noa_A3_lachen'],
|
||||||
[z.e4.start, 'noa_A3_lachen'],
|
[z.e4.start, 'noa_A3_lachen'],
|
||||||
];
|
];
|
||||||
// Hocke: Clip vorwärts (runter) oder rückwärts (aufstehen); sonst Standbilder
|
// Hocke: Abschnitte, in denen Noa hockt; Wechsel als weiche Überblendung der Standbilder
|
||||||
const hocke = (() => {
|
// (der Hinsetz-Clip ist anders gezeichnet), dabei kurz absinken bzw. sich strecken
|
||||||
const w = (start: number, auf: boolean) => ({start, auf});
|
const HOCKE_BLENDE = 14;
|
||||||
if (f < z.n1.start + 30) return w(-1000, false);
|
const hockZeiten: [number, number][] = [
|
||||||
if (f < z.n1.start + 30 + NOA_HOCKE_FRAMES) return w(z.n1.start + 30, true);
|
[-1000, z.n1.start + 30],
|
||||||
if (f >= z.n2.start - 15 && f < z.c5.ende) return w(z.n2.start - 15, false);
|
[z.n2.start - 15, z.c5.ende],
|
||||||
if (f >= z.c5.ende && f < z.c5.ende + NOA_HOCKE_FRAMES) return w(z.c5.ende, true);
|
[zurueck, z.e4.start],
|
||||||
if (f >= zurueck && f < z.e4.start) return w(-1000, false);
|
];
|
||||||
if (f >= z.e4.start && f < z.e4.start + NOA_HOCKE_FRAMES) return w(z.e4.start, true);
|
const inHocke = hockZeiten.some(([a, b]) => f >= a && f < b);
|
||||||
return null;
|
const stehPose = (t: number) => noaStehPose.filter(([s]) => s <= t).pop()![1];
|
||||||
})();
|
const noaPose: [number, string][] = [...noaStehPose, ...hockZeiten.flatMap(([a, b]): [number, string][] => [[a, 'noa_B_hocken'], [b, stehPose(b)]])].sort((p, q) => p[0] - q[0]);
|
||||||
|
const bump = (t: number) => (f >= t && f < t + HOCKE_BLENDE ? Math.sin(((f - t) / HOCKE_BLENDE) * Math.PI) : 0);
|
||||||
|
const hockeStauch = hockZeiten.reduce((s, [a, b]) => s * (1 - 0.05 * bump(a)) * (1 + 0.03 * bump(b)), 1);
|
||||||
// beim Hinlegen jedes Blatts kurz nach vorn beugen
|
// beim Hinlegen jedes Blatts kurz nach vorn beugen
|
||||||
const beugen = zaehlen.reduce((s, c) => s + Math.max(0, Math.sin(Math.min(1, Math.max(0, (f - c + 8) / 16)) * Math.PI)), 0);
|
const beugen = zaehlen.reduce((s, c) => s + Math.max(0, Math.sin(Math.min(1, Math.max(0, (f - c + 8) / 16)) * Math.PI)), 0);
|
||||||
// beim Nestbau schieben (vor und zurück)
|
// beim Nestbau schieben (vor und zurück)
|
||||||
|
|
@ -104,10 +106,12 @@ export const Szene4: React.FC = () => {
|
||||||
const pinoGeht = (f > pinoGehtVon && f < pinoGehtBis) || (f > zurueck + 10 && f < zurueck + 60);
|
const pinoGeht = (f > pinoGehtVon && f < pinoGehtBis) || (f > zurueck + 10 && f < zurueck + 60);
|
||||||
const pinoSpiegel = f > pinoGehtVon && f < pinoGehtBis; // nach rechts
|
const pinoSpiegel = f > pinoGehtVon && f < pinoGehtBis; // nach rechts
|
||||||
const imNest = f >= z.bild5.start;
|
const imNest = f >= z.bild5.start;
|
||||||
|
// Pino sitzt beim Zählen rechts von Noa: seine Bilder schauen nach rechts, darum gespiegelt (schaut zu ihr)
|
||||||
|
const pinoZuNoa = f >= pinoGehtBis && f <= zurueck + 10;
|
||||||
const pinoPose: [number, string][] = [
|
const pinoPose: [number, string][] = [
|
||||||
[0, 'pino_A1_augen-zu'],
|
[0, 'pino_A1_augen-zu'],
|
||||||
[z.n1.start + 20, 'pino_A2_augen-halb'],
|
[z.n1.start + 20, 'pino_A2_augen-halb'],
|
||||||
[pinoGehtBis, 'pino_A5_blick-links'],
|
[pinoGehtBis, 'pino_A6_blick-rechts'],
|
||||||
...zaehlen.map((c) => [c, 'pino_A4_staunen'] as [number, string]),
|
...zaehlen.map((c) => [c, 'pino_A4_staunen'] as [number, string]),
|
||||||
[z.c5.ende, 'pino_A3_lachen'],
|
[z.c5.ende, 'pino_A3_lachen'],
|
||||||
[niesen + 4, 'pino_B2_erschrocken'],
|
[niesen + 4, 'pino_B2_erschrocken'],
|
||||||
|
|
@ -217,11 +221,16 @@ 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} />
|
<Geher figur="noa" spiegeln x={noaX} y={BODEN} tempo={1.15} />
|
||||||
) : hocke ? (
|
|
||||||
<NoaHocke start={hocke.start} auf={hocke.auf} x={noaX + schieben * 10} y={BODEN} stauchY={1 - 0.03 * beugen} drehung={beugen * 3 + schieben * 2} />
|
|
||||||
) : (
|
) : (
|
||||||
<Figur pose={noaPose} x={noaX} y={BODEN} stauchY={atmen(f, 2)} />
|
<Figur
|
||||||
|
pose={noaPose}
|
||||||
|
x={noaX + (inHocke ? schieben * 10 : 0)}
|
||||||
|
y={BODEN}
|
||||||
|
stauchY={atmen(f, 2) * hockeStauch * (inHocke ? 1 - 0.03 * beugen : 1)}
|
||||||
|
drehung={inHocke ? beugen * 3 + schieben * 2 : 0}
|
||||||
|
blende={HOCKE_BLENDE}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{/* das große Blatt aus Szene 3 bleibt liegen */}
|
{/* das große Blatt aus Szene 3 bleibt liegen */}
|
||||||
{f < zurueck + 40 && <Blatt x={GROSSES_BLATT_X} y={BODEN - 8} r={88} s={2.6} farbe="#c8642c" deckkraft={f > zurueck ? 1 - (f - zurueck) / 40 : 1} />}
|
{f < zurueck + 40 && <Blatt x={GROSSES_BLATT_X} y={BODEN - 8} r={88} s={2.6} farbe="#c8642c" deckkraft={f > zurueck ? 1 - (f - zurueck) / 40 : 1} />}
|
||||||
|
|
@ -234,6 +243,7 @@ export const Szene4: React.FC = () => {
|
||||||
) : (
|
) : (
|
||||||
<Figur
|
<Figur
|
||||||
pose={pinoPose}
|
pose={pinoPose}
|
||||||
|
spiegeln={pinoZuNoa}
|
||||||
x={pinoX}
|
x={pinoX}
|
||||||
y={BODEN - 6 * sitzt + zaehlHuepfer + huepfer(f, niesen + 2, 30, 12) + insNest}
|
y={BODEN - 6 * sitzt + zaehlHuepfer + huepfer(f, niesen + 2, 30, 12) + insNest}
|
||||||
stauchY={atmen(f, 1) * hopfStauch(f, niesen + 2) * hopfStauch(f, z.bild5.start - 20, 20)}
|
stauchY={atmen(f, 1) * hopfStauch(f, niesen + 2) * hopfStauch(f, z.bild5.start - 20, 20)}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue