import { useEffect, useRef } from 'react'; import { Animated, Dimensions, Image, Text, View } from 'react-native'; import Svg, { Defs, RadialGradient, Rect, Stop } from 'react-native-svg'; import { useTranslation } from 'react-i18next'; // Phase-Timings (ms ab Mount) — 1:1 portiert aus apps/rebreak/app/components/AppSplash.vue const T_GLOW = 0; const T_NAME = 300; const T_LOGO = 700; const T_PULSE = 1100; const T_TAGLINE = 1300; const T_SUB = 1700; const T_HOLD_END = 3200; const T_LEAVE_DUR = 500; const { width: SW, height: SH } = Dimensions.get('window'); type ParticleConfig = { size: number; top?: number; bottom?: number; left?: number; right?: number; duration: number; delay: number; }; const PARTICLES: ParticleConfig[] = [ { size: 180, top: -40, left: -60, duration: 7000, delay: 0 }, { size: 120, bottom: SH * 0.1, right: -30, duration: 9000, delay: 1500 }, { size: 80, top: SH * 0.35, left: SW * 0.08, duration: 11000, delay: 800 }, { size: 60, bottom: SH * 0.2, left: SW * 0.2, duration: 8000, delay: 2200 }, { size: 100, top: SH * 0.15, right: SW * 0.1, duration: 10000, delay: 400 }, ]; function Particle({ config }: { config: ParticleConfig }) { const translateY = useRef(new Animated.Value(0)).current; const scale = useRef(new Animated.Value(1)).current; const opacity = useRef(new Animated.Value(0.6)).current; useEffect(() => { const animate = () => { Animated.loop( Animated.sequence([ Animated.parallel([ Animated.timing(translateY, { toValue: 18, duration: config.duration, useNativeDriver: true, }), Animated.timing(scale, { toValue: 1.1, duration: config.duration, useNativeDriver: true, }), Animated.timing(opacity, { toValue: 1, duration: config.duration, useNativeDriver: true, }), ]), Animated.parallel([ Animated.timing(translateY, { toValue: 0, duration: config.duration, useNativeDriver: true, }), Animated.timing(scale, { toValue: 1, duration: config.duration, useNativeDriver: true, }), Animated.timing(opacity, { toValue: 0.6, duration: config.duration, useNativeDriver: true, }), ]), ]), ).start(); }; const t = setTimeout(animate, config.delay); return () => clearTimeout(t); }, [config, translateY, scale, opacity]); return ( ); } export function BrandSplash() { const { t } = useTranslation(); // Phase-Opacity-Animationen const containerOpacity = useRef(new Animated.Value(1)).current; const glowCenterOpacity = useRef(new Animated.Value(0)).current; const glowCenterScale = useRef(new Animated.Value(0.6)).current; const glowTopOpacity = useRef(new Animated.Value(0.5)).current; const nameOpacity = useRef(new Animated.Value(0)).current; const nameTranslateY = useRef(new Animated.Value(12)).current; const logoOpacity = useRef(new Animated.Value(0)).current; const logoScale = useRef(new Animated.Value(0.82)).current; const logoTranslateY = useRef(new Animated.Value(8)).current; const logoPulse = useRef(new Animated.Value(1)).current; const taglineOpacity = useRef(new Animated.Value(0)).current; const taglineTranslateY = useRef(new Animated.Value(8)).current; const subOpacity = useRef(new Animated.Value(0)).current; const subTranslateY = useRef(new Animated.Value(6)).current; const footerOpacity = useRef(new Animated.Value(0)).current; useEffect(() => { // Top-glow breath loop (4s alternating) — startet sofort Animated.loop( Animated.sequence([ Animated.timing(glowTopOpacity, { toValue: 0.9, duration: 2000, useNativeDriver: true, }), Animated.timing(glowTopOpacity, { toValue: 0.5, duration: 2000, useNativeDriver: true, }), ]), ).start(); const ease = (toValue: number, duration: number) => ({ toValue, duration, useNativeDriver: true, }); // Phase 1: glow center bloom (T=0) Animated.parallel([ Animated.timing(glowCenterOpacity, ease(1, 900)), Animated.timing(glowCenterScale, ease(1, 900)), ]).start(); // Phase 2: Name fade-in (T=300) setTimeout(() => { Animated.parallel([ Animated.timing(nameOpacity, ease(1, 600)), Animated.timing(nameTranslateY, ease(0, 600)), ]).start(); }, T_NAME); // Phase 3: Logo bouncy scale-in (T=700) setTimeout(() => { Animated.parallel([ Animated.timing(logoOpacity, ease(1, 650)), Animated.spring(logoScale, { toValue: 1, useNativeDriver: true, friction: 6, tension: 80, }), Animated.timing(logoTranslateY, ease(0, 650)), ]).start(); }, T_LOGO); // Phase 3b: Logo breathing pulse (T=1100) setTimeout(() => { Animated.loop( Animated.sequence([ Animated.timing(logoPulse, { toValue: 1.04, duration: 1300, useNativeDriver: true, }), Animated.timing(logoPulse, { toValue: 1, duration: 1300, useNativeDriver: true, }), ]), ).start(); }, T_PULSE); // Phase 4: Tagline (T=1300) setTimeout(() => { Animated.parallel([ Animated.timing(taglineOpacity, ease(1, 550)), Animated.timing(taglineTranslateY, ease(0, 550)), ]).start(); }, T_TAGLINE); // Phase 5: Sub-text + Footer (T=1700) setTimeout(() => { Animated.parallel([ Animated.timing(subOpacity, ease(1, 500)), Animated.timing(subTranslateY, ease(0, 500)), Animated.timing(footerOpacity, ease(1, 600)), ]).start(); }, T_SUB); // Phase 7: whole-screen fade-out (T=3200, dauert 500ms) const fadeOutTimer = setTimeout(() => { Animated.timing(containerOpacity, { toValue: 0, duration: T_LEAVE_DUR, useNativeDriver: true, }).start(); }, T_HOLD_END); return () => clearTimeout(fadeOutTimer); }, [ glowTopOpacity, glowCenterOpacity, glowCenterScale, nameOpacity, nameTranslateY, logoOpacity, logoScale, logoTranslateY, logoPulse, taglineOpacity, taglineTranslateY, subOpacity, subTranslateY, footerOpacity, containerOpacity, ]); return ( {/* Top breathing radial-gradient ellipse (#1e3a8a auf transparent) */} {/* Center indigo halo — bloomt rein wenn Logo erscheint */} {/* Floating particles (5 Stück) */} {PARTICLES.map((p, i) => ( ))} {/* Content-Column */} {/* App-Name */} {t('appHeader.appName')} {/* Logo (mit Pulse + Bouncy Entry) */} {/* Tagline */} {t('splash.tagline')} {/* Sub-text */} {t('splash.subtitle')} {/* Footer */} {t('splash.madeInGermany')} ); }