Files

156 lines
4.3 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { StyleSheet, Dimensions, Text, View, StatusBar, Animated, Easing } from 'react-native';
import { useAppTheme } from '../context/ThemeContext';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { useAppConfig } from '../context/ConfigContext';
import { Image } from 'expo-image';
const { width, height } = Dimensions.get('window');
interface AnimatedSplashProps {
onAnimationComplete: () => void;
}
export const AnimatedSplash: React.FC<AnimatedSplashProps> = ({ onAnimationComplete }) => {
const { isDark, colors } = useAppTheme();
const { config } = useAppConfig();
// Using standard React Native Animated for safety
const logoScale = useRef(new Animated.Value(0.5)).current;
const logoOpacity = useRef(new Animated.Value(0)).current;
const containerOpacity = useRef(new Animated.Value(1)).current;
const textOpacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
// 1. Entry Animation
Animated.parallel([
Animated.timing(logoOpacity, { toValue: 1, duration: 1000, useNativeDriver: true }),
Animated.timing(logoScale, {
toValue: 1,
duration: 1200,
easing: Easing.out(Easing.back(1.5)),
useNativeDriver: true
}),
Animated.timing(textOpacity, {
toValue: 1,
duration: 800,
delay: 1000,
useNativeDriver: true
})
]).start();
// 2. Clear Exit
const timer = setTimeout(() => {
Animated.timing(containerOpacity, {
toValue: 0,
duration: 600,
useNativeDriver: true
}).start(() => {
// Delay to ensure the animation frame finishes before state change triggers unmount
setTimeout(() => {
onAnimationComplete();
}, 100);
});
}, 3500);
return () => clearTimeout(timer);
}, []);
return (
<Animated.View style={[styles.container, { opacity: containerOpacity, backgroundColor: isDark ? '#020617' : '#F8FAFC' }]}>
<StatusBar hidden />
<View style={styles.centerBox}>
<Animated.View style={[
styles.logoWrapper,
{
opacity: logoOpacity,
transform: [{ scale: logoScale }],
}
]}>
{config?.branding?.logo_url ? (
<Image
source={{ uri: config.branding.logo_url }}
style={{ width: 100, height: 100 }}
contentFit="contain"
/>
) : (
<MaterialCommunityIcons name="shield-check" size={80} color={isDark ? '#38BDF8' : colors.primary || '#6C63FF'} />
)}
</Animated.View>
<Animated.View style={[
styles.textWrapper,
{
opacity: textOpacity,
transform: [{
translateY: textOpacity.interpolate({
inputRange: [0, 1],
outputRange: [30, 0]
})
}]
}
]}>
<View style={styles.brandContainer}>
<Text style={[styles.brandAI, { color: isDark ? '#FFF' : '#0F172A' }]}>
{config?.branding?.app_name || 'biiproject'}
</Text>
</View>
<View style={styles.taglineBox}>
<View style={styles.line} />
<Text style={styles.tagline}>{config?.branding?.app_tagline || 'DIGITAL SOLUTIONS'}</Text>
<View style={styles.line} />
</View>
</Animated.View>
</View>
</Animated.View>
);
};
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
zIndex: 9999,
},
centerBox: {
alignItems: 'center',
},
logoWrapper: {
width: 150,
height: 150,
justifyContent: 'center',
alignItems: 'center',
},
brandContainer: {
flexDirection: 'row',
alignItems: 'baseline',
marginTop: 35,
},
brandAI: {
fontSize: 46,
fontFamily: 'Outfit_700Bold',
letterSpacing: 2,
},
textWrapper: {
alignItems: 'center',
},
taglineBox: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 15,
},
line: {
width: 25,
height: 1,
backgroundColor: '#E2E8F0',
marginHorizontal: 12,
},
tagline: {
fontSize: 11,
color: '#94A3B8',
fontFamily: 'Outfit_700Bold',
letterSpacing: 3,
}
});