import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, spring, Img, staticFile, Sequence, } from "remotion"; import { Audio } from "@remotion/media"; import { CountryBar } from "../../../components/CountryBar"; import { SparklineChart } from "../../../components/SparklineChart"; // Spring configurations const SNAPPY = { damping: 15, stiffness: 80 }; const BOUNCY = { damping: 12 }; // Stagger delay between country items (in frames) const COUNTRY_STAGGER_DELAY = 10; // Country statistics data const COUNTRY_DATA = [ { name: "Germany", flagEmoji: "🇩🇪", userCount: 458, sparklineData: [80, 120, 180, 220, 280, 320, 350, 390, 420, 445, 455, 458], }, { name: "Austria", flagEmoji: "🇦🇹", userCount: 59, sparklineData: [10, 15, 22, 28, 35, 40, 45, 50, 54, 57, 58, 59], }, { name: "Switzerland", flagEmoji: "🇨🇭", userCount: 34, sparklineData: [5, 8, 12, 15, 18, 22, 25, 28, 30, 32, 33, 34], }, { name: "Luxembourg", flagEmoji: "🇱🇺", userCount: 8, sparklineData: [1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8], }, { name: "Bulgaria", flagEmoji: "🇧🇬", userCount: 7, sparklineData: [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7], }, { name: "Spain", flagEmoji: "🇪🇸", userCount: 3, sparklineData: [0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3], }, ]; // Maximum count for calculating relative bar widths const MAX_USER_COUNT = Math.max(...COUNTRY_DATA.map((c) => c.userCount)); /** * CountryStatsSceneMobile - Scene 5: Top Länder for Mobile (12 seconds / 360 frames @ 30fps) * * Mobile layout adaptations: * - Single column layout instead of 2-column grid * - Smaller bar widths optimized for 1080px width * - Sparkline placed below each country bar * - Reduced spacing and text sizes */ export const CountryStatsSceneMobile: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // 3D Perspective entrance animation (0-60 frames) const perspectiveSpring = spring({ frame, fps, config: { damping: 20, stiffness: 60 }, }); const perspectiveX = interpolate(perspectiveSpring, [0, 1], [20, 0]); const perspectiveScale = interpolate(perspectiveSpring, [0, 1], [0.9, 1]); const perspectiveOpacity = interpolate(perspectiveSpring, [0, 1], [0, 1]); // Header entrance animation (delayed) const headerDelay = Math.floor(0.3 * fps); const headerSpring = spring({ frame: frame - headerDelay, fps, config: SNAPPY, }); const headerOpacity = interpolate(headerSpring, [0, 1], [0, 1]); const headerY = interpolate(headerSpring, [0, 1], [-40, 0]); // Subtitle animation (slightly more delayed) const subtitleDelay = Math.floor(0.5 * fps); const subtitleSpring = spring({ frame: frame - subtitleDelay, fps, config: SNAPPY, }); const subtitleOpacity = interpolate(subtitleSpring, [0, 1], [0, 1]); const subtitleY = interpolate(subtitleSpring, [0, 1], [20, 0]); // Base delay for country items const countryBaseDelay = Math.floor(1 * fps); // Subtle glow pulse const glowIntensity = interpolate( Math.sin(frame * 0.05), [-1, 1], [0.3, 0.6] ); // Total users count animation const totalUsers = COUNTRY_DATA.reduce((sum, c) => sum + c.userCount, 0); const totalDelay = Math.floor(0.8 * fps); const totalSpring = spring({ frame: frame - totalDelay, fps, config: { damping: 18, stiffness: 70 }, durationInFrames: 60, }); const displayTotal = Math.round(totalSpring * totalUsers); return ( {/* Audio: success-chime for each country entrance */} {COUNTRY_DATA.map((_, index) => ( ))} {/* Audio: slide-in for section entrance */} {/* Wallpaper Background */}
{/* Dark gradient overlay */}
{/* 3D Perspective Container */}
{/* Main Content - Centered for mobile */}
{/* Section Header */}

Community nach Ländern

Die deutschsprachige Bitcoin-Community wächst

{/* Total Users Badge */} {/* Countries List - Single column for mobile */}
{COUNTRY_DATA.map((country, index) => ( ))}
{/* Vignette overlay */}
); }; /** * Total users badge component for mobile */ type TotalUsersBadgeMobileProps = { totalUsers: number; delay: number; glowIntensity: number; }; const TotalUsersBadgeMobile: React.FC = ({ totalUsers, delay, glowIntensity, }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const adjustedFrame = Math.max(0, frame - delay); const badgeSpring = spring({ frame: adjustedFrame, fps, config: BOUNCY, }); const badgeScale = interpolate(badgeSpring, [0, 1], [0.8, 1]); const badgeOpacity = interpolate(badgeSpring, [0, 1], [0, 1]); return (
{totalUsers} Nutzer weltweit
); }; /** * Country row with bar and sparkline for mobile */ type CountryRowMobileProps = { country: { name: string; flagEmoji: string; userCount: number; sparklineData: number[]; }; maxCount: number; delay: number; glowIntensity: number; }; const CountryRowMobile: React.FC = ({ country, maxCount, delay, glowIntensity, }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const adjustedFrame = Math.max(0, frame - delay); const rowSpring = spring({ frame: adjustedFrame, fps, config: SNAPPY, }); const rowOpacity = interpolate(rowSpring, [0, 1], [0, 1]); const rowX = interpolate(rowSpring, [0, 1], [-50, 0]); // Sparkline delay (appears slightly after the bar) const sparklineDelay = delay + 15; return (
{/* Country Bar - Compact for mobile */}
{/* Sparkline Chart - Smaller for mobile */}
); }; /** * Globe icon SVG */ const GlobeIcon: React.FC = () => ( );