Folge 1, Szene 2, Rückmeldung 1: Laubhaufen aus Ahorn- und Buchenblättern, Noas Füße im Haufen verdeckt, Hocke über Clip, Pino rollt sich auf und schaut zu Noa

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lara 2026-11-02 12:29:00 +01:00
parent 336c2121d5
commit 132e969d03
2 changed files with 62 additions and 25 deletions

View file

@ -308,25 +308,45 @@ export const Blume: React.FC<{x: number; y: number; s?: number; r?: number}> = (
</svg>
);
// Folge 1: Laubhaufen (Fuß bei x, y; ca. 340 × 140 px bei s = 1).
// Ahornblatt (Spitze oben, ca. 90 px hoch bei s = 1) und schmales Buchenblatt, für den Laubhaufen
const AHORN = 'M0,40 L4,18 L22,27 L18,12 L40,5 L30,-2 L37,-21 L18,-14 L16,-31 L6,-22 L0,-46 L-6,-22 L-16,-31 L-18,-14 L-37,-21 L-30,-2 L-40,5 L-18,12 L-22,27 L-4,18 Z';
const AHORN_ADERN = 'M0,52 L0,-38 M0,10 L30,-14 M0,10 L-30,-14 M0,18 L18,24 M0,18 L-18,24';
const BUCHE = 'M0,-44 C 22,-30 24,0 0,30 C -24,0 -22,-30 0,-44 Z';
const BUCHE_ADERN = 'M0,40 L0,-36 M0,-14 L12,-24 M0,-2 L14,-12 M0,-8 L-12,-18 M0,4 L-13,-6';
const HAUFEN_HELL = ['#e07a2e', '#d4522a', '#e9a83a', '#c2412a', '#f0c048', '#d98a3a', '#b8662a'];
const HAUFEN_DUNKEL = ['#9e4a26', '#8a3a22', '#a8682e', '#7a3a24'];
// Folge 1: Laubhaufen aus großen Herbstblättern (Fuß bei x, y; ca. 400 × 190 px bei s = 1).
// Reicht unten 45 px unter den Fußpunkt, damit Füße darin verschwinden.
// `wackeln` 0 … 1 lässt ihn zittern, `flach` 0 … 1 drückt ihn zusammen (Noa springt hinein).
export const Laubhaufen: React.FC<{x: number; y: number; s?: number; wackeln?: number; flach?: number}> = ({x, y, s = 1, wackeln = 0, flach = 0}) => {
const frame = useCurrentFrame();
const zitter = wackeln * Math.sin(frame * 1.3) * 3;
// hinten/oben zuerst zeichnen, vorne/unten zuletzt
const blaetter = new Array(120).fill(0).map((_, i) => {
const r = (k: string) => random(`lh${k}${i}`);
const u = r('u') * 2.2 - 1.1;
const kuppe = Math.pow(Math.max(0, 1 - u * u), 0.6) * 150;
const ly = 40 - r('y') * (kuppe + 40);
return {i, u, lx: u * 180, ly, ahorn: r('a') < 0.62, s: 0.85 + r('s') * 0.65, w: r('w') * 360, dunkel: ly < -kuppe * 0.55 ? false : r('d') < 0.3, farbe: r('f')};
});
blaetter.sort((a, b) => a.ly - b.ly);
return (
<svg
width={400}
height={180}
viewBox="-200 -170 400 180"
style={{position: 'absolute', left: x - 200, top: y - 170, overflow: 'visible', transform: `scale(${s * (1 + flach * 0.08)}, ${s * (1 - flach * 0.3)}) rotate(${zitter}deg)`, transformOrigin: '50% 95%'}}
width={460}
height={260}
viewBox="-230 -205 460 260"
style={{position: 'absolute', left: x - 230, top: y - 205, overflow: 'visible', transform: `scale(${s * (1 + flach * 0.08)}, ${s * (1 - flach * 0.25)}) rotate(${zitter}deg)`, transformOrigin: '50% 79%'}}
>
<path d="M -175 0 C -160 -90, -80 -140, 0 -140 C 80 -140, 160 -90, 175 0 Z" fill="#a8602c" />
{new Array(70).fill(0).map((_, i) => {
const u = random('lh' + i) * 2 - 1;
const h = 1 - u * u;
const bx = u * 165;
const by = -random('lhy' + i) * 132 * h - 4;
return <ellipse key={i} cx={bx} cy={by} rx={16 + random('lhr' + i) * 8} ry={8} transform={`rotate(${random('lhd' + i) * 180} ${bx} ${by})`} fill={LAUB[i % LAUB.length]} />;
<ellipse cx={0} cy={36} rx={210} ry={22} fill="rgba(70,45,25,0.25)" />
{blaetter.map((b) => {
const liste = b.dunkel ? HAUFEN_DUNKEL : HAUFEN_HELL;
return (
<g key={b.i} transform={`translate(${b.lx} ${b.ly}) rotate(${b.w}) scale(${b.s})`}>
<path d={b.ahorn ? AHORN : BUCHE} fill={liste[Math.floor(b.farbe * liste.length)]} stroke="rgba(90,40,20,0.35)" strokeWidth={1.5} strokeLinejoin="round" />
<path d={b.ahorn ? AHORN_ADERN : BUCHE_ADERN} stroke="rgba(110,50,20,0.4)" strokeWidth={1.6} fill="none" strokeLinecap="round" />
</g>
);
})}
</svg>
);

View file

@ -1,7 +1,7 @@
import React from 'react';
import {useCurrentFrame} from 'remotion';
import {kurve} from '../bewegung';
import {Figur, Geher, Kamera} from '../baukasten/figur';
import {ClipFigur, Figur, Geher, Kamera, NOA_HOCKE_FRAMES, NoaHocke} from '../baukasten/figur';
import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt, ruck} from '../baukasten/helfer';
import {Apfel, Blaetterregen, Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur';
import {BODEN, Ort} from '../baukasten/orte';
@ -16,7 +16,7 @@ export const APFEL = {x: 560, y: 205}; // der letzte Apfel, ganz oben im Baum
export const BLUME_X = 1560; // blaue Blume am Zaun
export const BLUME_Y = 805;
export const NOA_X = 960; // Noa neben dem Haufen
export const PINO_HAUFEN_Y = BODEN - 92; // Pino sitzt oben im Haufen
export const PINO_HAUFEN_Y = BODEN - 128; // Pino steckt oben im Haufen (als Kugel)
export const S2 = plane('s2', [
{id: 'bild0', dauer: 2.2},
@ -71,16 +71,18 @@ export const Szene2: React.FC = () => {
[schreck, 'noa_B2_erschrocken'],
[z.n2.start + 6, 'noa_A4_staunen'],
[z.e6.start, 'noa_A6_blick-rechts'],
[hocke, 'noa_B_hocken'],
[aufstehen, 'noa_A3_lachen'],
[z.p2.start + 10, 'noa_A6_blick-rechts'],
];
// Hocke: Clip „hinsetzen“ bis zur Hocke, zum Aufstehen rückwärts
const inHocke = f >= hocke && f < aufstehen + NOA_HOCKE_FRAMES;
const hockeStart = f < aufstehen ? hocke : aufstehen;
const noaY =
BODEN +
sprungY +
huepfer(f, raus, 60, 16) +
ruck(f, schreck, 3) +
huepfer(f, z.n4.start + 20, 14, 10);
huepfer(f, aufstehen + NOA_HOCKE_FRAMES + 4, 14, 10);
// Laubhaufen: wird bei der Landung gestaucht, später raschelt er
const flach = kurve(f, [
@ -96,24 +98,28 @@ export const Szene2: React.FC = () => {
]);
// Pino: als Stachelkugel im Haufen, kommt hoch, öffnet sich bei bild4
const auf = z.bild4.start; // Einroll-Clip rückwärts
const AUF_FRAMES = 75; // 121 Clip-Bilder (24 fps) bei Tempo 2
const oeffnet = f >= auf && f < auf + AUF_FRAMES;
const zuNoa = f >= auf; // die Bilder schauen nach rechts, Noa sitzt links
const pinoY = kurve(f, [
[schreck - 10, PINO_HAUFEN_Y + 110],
[schreck - 10, PINO_HAUFEN_Y + 140],
[schreck + 6, PINO_HAUFEN_Y],
[z.bild4.start + 14, PINO_HAUFEN_Y],
[z.bild4.start + 40, PINO_HAUFEN_Y - 42],
[auf + 20, PINO_HAUFEN_Y],
[auf + AUF_FRAMES, PINO_HAUFEN_Y - 40],
]);
const pinoDa = f >= schreck - 10;
const pinoPose: [number, string][] = [
[0, 'pino_A_kugel'],
[z.bild4.start + 20, 'pino_ref_sitzend'],
[auf + AUF_FRAMES, 'pino_ref_sitzend'],
[z.p1.start, 'pino_B_blick-nach-oben'],
[z.p1.start + 40, 'pino_A4_staunen'],
[z.p2.start, 'pino_B_blick-nach-oben'],
[z.p2.ende - 10, 'pino_A3_lachen'],
];
// Stacheln zittern vor Angst, solange er eine Kugel ist
const angst = f < z.bild4.start + 20 ? Math.sin(f * 1.7) * 1.5 : 0;
const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, schreck) * (f >= z.bild4.start && f < z.bild4.start + 24 ? kurve(f, [[z.bild4.start, 0.9], [z.bild4.start + 24, 1]]) : 1);
const angst = f < auf ? Math.sin(f * 1.7) * 1.5 : 0;
const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, schreck);
const kam = kamFahrt(f, [
[0, 960, 540, 1.0],
@ -135,11 +141,22 @@ export const Szene2: React.FC = () => {
<Ort name="garten_herbst" />
<Apfel x={APFEL.x} y={APFEL.y} s={0.9} />
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} />
{pinoDa && <Figur pose={pinoPose} x={HAUFEN_X + 10} y={pinoY} stauchY={pinoStauch} drehung={angst} schatten={false} blende={8} />}
{pinoDa &&
(oeffnet ? (
<ClipFigur name="pino_C7_einrollen" start={auf} tempo={2} rueckwaerts spiegeln x={HAUFEN_X + 10} y={pinoY} schatten={false} ersatz="pino_ref_sitzend" />
) : (
<Figur pose={pinoPose} spiegeln={zuNoa} x={HAUFEN_X + 10} y={pinoY} stauchY={pinoStauch} drehung={angst} schatten={false} blende={8} />
))}
{/* Im Haufen steht Noa hinter den vorderen Blättern */}
{imHaufen && <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} schatten={false} />}
<Laubhaufen x={HAUFEN_X} y={BODEN + 12} wackeln={wackeln} flach={flach} />
{imHaufen ? null : noaGeht ? <Geher figur="noa" spiegeln x={noaX} y={BODEN} /> : <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} />}
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} wackeln={wackeln} flach={flach} />
{imHaufen ? null : noaGeht ? (
<Geher figur="noa" spiegeln x={noaX} y={BODEN} />
) : inHocke ? (
<NoaHocke start={hockeStart} auf={f >= aufstehen} x={noaX} y={BODEN} />
) : (
<Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} />
)}
<Blaetterregen start={landung - 2} x0={HAUFEN_X - 60} y0={BODEN - 60} boden={BODEN - 10} anzahl={28} aus={z.e4.start} />
</Kamera>
<Blaetterwind anzahl={5} />