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')}
);
}