import React from 'react'; import {AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion'; import {SCHRIFT, Untertitel} from '../baukasten/untertitel'; import type {Geplant} from '../baukasten/zeit'; import {TEXTE} from './plan'; // Lern-Shorts im Hochformat (1080 × 1920). Die Szene spielt auf der gewohnten Bühne (1920 × 1080, // Figuren und Orte wie in den Folgen); `Buehne` schneidet daraus den Hochformat-Ausschnitt. // Frage oben, Untertitel über dem Bereich, den YouTube unten mit Titel und Knöpfen verdeckt. export const W = 1080; export const H = 1920; export const VOLL = H / 1080; // kleinste Vergrößerung, bei der die Bühne das Hochformat füllt export const Buehne: React.FC<{x: number; y?: number; z?: number; children: React.ReactNode}> = ({x, y = 540, z = VOLL, children}) => { const halbB = W / 2 / z; const halbH = H / 2 / z; // Ist der Ausschnitt größer als die Bühne, bleibt sie mittig (oben/unten Papierrand) const bx = halbB * 2 >= 1920 ? 960 : Math.min(1920 - halbB, Math.max(halbB, x)); const by = halbH * 2 >= 1080 ? 540 : Math.min(1080 - halbH, Math.max(halbH, y)); return (
{children}
); }; const KARTE = { background: 'rgba(255, 250, 240, 0.92)', borderRadius: 40, boxShadow: '0 8px 30px rgba(60, 40, 20, 0.22)', fontFamily: SCHRIFT, textAlign: 'center', } as const; // Frage oben, darunter klein der Kanalname export const FrageKarte: React.FC<{text: string}> = ({text}) => { const f = useCurrentFrame(); const {fps} = useVideoConfig(); const s = spring({frame: f - 4, fps, config: {damping: 14}}); return (
{text}
{TEXTE.zeilen.kanal}
); }; // Untertitel der Folgen, nach oben versetzt (unten liegen im Shorts-Player Titel und Knöpfe); // das Mitmach-„?“ erscheint unter der Frage-Karte statt darauf export const HochUntertitel: React.FC<{zeilen: Geplant[]}> = ({zeilen}) => (
); // Runde Bildkarte, die bei `ab` aufploppt (Bildschirm-Koordinaten des Hochformats) export const BildKarte: React.FC<{x: number; y: number; ab: number; r?: number; aus?: number; children: React.ReactNode}> = ({x, y, ab, r = 150, aus = 1e9, children}) => { const f = useCurrentFrame(); const {fps} = useVideoConfig(); const s = spring({frame: f - ab, fps, config: {damping: 11}}); const weg = interpolate(f, [aus, aus + 10], [1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); if (f < ab) return null; return (
{children}
); }; // Großes Wort zum Mitsagen (klein – mittel – groß) // `puls`: Zeitpunkte, an denen das Wort noch einmal kurz groß wird (beim Mitsagen) export const Wort: React.FC<{text: string; x: number; y: number; ab: number; puls?: number[]; groesse?: number; farbe?: string}> = ({text, x, y, ab, puls = [], groesse = 90, farbe = '#c8502c'}) => { const f = useCurrentFrame(); const {fps} = useVideoConfig(); if (f < ab) return null; const pulsen = puls.reduce((m, p) => m + 0.35 * Math.max(0, Math.sin(Math.PI * Math.min(1, Math.max(0, (f - p) / 18)))), 0); const s = spring({frame: f - ab, fps, config: {damping: 10}}) + pulsen; return (
{text}
); }; // `hintergrund`: füllt oben und unten, wenn die Bühne das Hochformat nicht ganz ausfüllt export const Hochformat: React.FC<{hintergrund?: string; children: React.ReactNode}> = ({hintergrund = '#f4ead8', children}) => ( {children} );