Schattenspiel: Mitmach-Karte mit gezeichneter Hand (wie im Zähllied) für Hase und Igel; Hand nach baukasten/hand.tsx

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lara 2026-11-06 08:19:16 +00:00
parent f303ec8d7a
commit c937844f94
3 changed files with 85 additions and 41 deletions

View file

@ -0,0 +1,48 @@
import React from 'react';
// Gezeichnete Hand (Bilderbuchstil) für Mitmach-Karten: Zählen mit den Fingern (Lied 1),
// Hand-Schatten (Folge 8). `hoch` sagt je Finger (Daumen zuerst), ob er gestreckt ist;
// `neu` = Index des Fingers, der gerade hochschnellt, `wachsen` 0–1 dessen Fortschritt;
// `gespreizt` fächert die gestreckten Finger auf (Igel-Stacheln).
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 SPREIZ = [-10, -16, -2, 12, 22];
export const Hand: React.FC<{hoch: boolean[]; neu?: number; wachsen?: number; spiegeln?: boolean; gespreizt?: boolean}> = ({hoch, neu = -1, wachsen = 1, spiegeln = false, gespreizt = false}) => (
<svg width={170} height={230} viewBox="0 0 160 220" style={{transform: spiegeln ? 'scaleX(-1)' : undefined, overflow: 'visible'}}>
{FINGER.map((fi, k) => {
const l = hoch[k] ? (k === neu ? 18 + (fi.l - 18) * wachsen : fi.l) : 18;
const fussY = k === 0 ? 150 : 112;
const dreh = fi.dreh + (gespreizt && hoch[k] ? SPREIZ[k] : 0);
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(${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) => (!hoch[k + 1] ? <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>
);
// Zählen: die ersten `finger` Finger gestreckt (Daumen = 1, wie in Deutschland üblich)
export const ZaehlHand: React.FC<{finger: number; neu: number; spiegeln?: boolean}> = ({finger, neu, spiegeln}) => (
<Hand hoch={[0, 1, 2, 3, 4].map((k) => k < finger)} neu={finger - 1} wachsen={neu} spiegeln={spiegeln} />
);

View file

@ -1,6 +1,8 @@
import React from 'react';
import {interpolate, useCurrentFrame} from 'remotion';
import {Hand} from '../baukasten/hand';
import {Blende, kamFahrt, punkt} from '../baukasten/helfer';
import {SCHRIFT} from '../baukasten/untertitel';
import {BODEN} from '../baukasten/orte';
import {POSEN_SKALA} from '../baukasten/figur';
import {Untertitel} from '../baukasten/untertitel';
@ -9,7 +11,8 @@ import {HandSchatten, NoasHand} from './teile';
import {PLATZ, Wandszene} from './wand';
// Szene 7: Schattenspiel. Noa macht mit der Hand vor der Laterne einen Hasen, dann einen Igel.
// Die Kinder machen mit (Pausen). Lulu streckt den Hals lang, ihr Schatten reicht bis zum Dach.
// Die Kinder machen mit (Pausen): Solange der Erzähler erklärt, zeigt oben links eine Karte die Hand
// (gezeichnet wie beim Zähllied), welche Finger hoch müssen. Lulu streckt den Hals lang.
export const S7 = plane('s7', [
{id: 'bild0', dauer: 1.2},
@ -32,6 +35,34 @@ const HAND = {x: 955, y: 770};
const HAND_S = 0.3;
const K = 3; // Hand nah am Licht → großer Schatten
// Mitmach-Karte oben links (oben rechts erscheint in der Pause das „?“)
const HandKarte: React.FC<{art: 'hase' | 'igel'; von: number; bis: number}> = ({art, von, bis}) => {
const f = useCurrentFrame();
const auf = interpolate(f, [von, von + 10, bis - 8, bis], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
if (auf <= 0) return null;
const wackeln = Math.sin(f * 0.18) * 3;
return (
<div
style={{
position: 'absolute',
left: 56,
top: 44,
padding: '18px 26px 10px',
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,
opacity: auf,
transform: `scale(${0.85 + 0.15 * auf}) rotate(${wackeln}deg)`,
transformOrigin: '0 0',
}}
>
<Hand hoch={art === 'hase' ? [false, true, true, false, false] : [true, true, true, true, true]} gespreizt />
</div>
);
};
export const Szene7: React.FC = () => {
const f = useCurrentFrame();
const {z, dauer} = S7;
@ -71,6 +102,8 @@ export const Szene7: React.FC = () => {
</>
}
/>
<HandKarte art="hase" von={z.e1.start} bis={z.e1.frei} />
<HandKarte art="igel" von={z.e3.start} bis={z.e3.frei} />
<Untertitel zeilen={S7.liste} />
<Blende dauer={dauer} rein={6} raus={14} />
</>

View file

@ -2,6 +2,7 @@ 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 {ZaehlHand} from '../../baukasten/hand';
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';
@ -179,44 +180,6 @@ const Liedapfel: React.FC<{i: number; f: number}> = ({i, f}) => {
);
};
// 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 => {
@ -259,8 +222,8 @@ const Zahltafel: React.FC = () => {
<>
<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 />}
<ZaehlHand finger={Math.min(n, 5)} neu={n <= 5 ? neu : 1} />
{n > 5 && <ZaehlHand finger={n - 5} neu={neu} spiegeln />}
</div>
</>
);