APP-PUSH和纯色小组件
This commit is contained in:
@@ -4,9 +4,9 @@ import { useFonts } from 'expo-font';
|
||||
import { Stack } from 'expo-router';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
import * as Notifications from 'expo-notifications';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import 'react-native-reanimated';
|
||||
import { AppState } from 'react-native';
|
||||
import { Animated, AppState, Image, StyleSheet, View } from 'react-native';
|
||||
|
||||
import { useColorScheme } from '@/components/useColorScheme';
|
||||
import { initI18n } from '@/src/i18n';
|
||||
@@ -41,6 +41,10 @@ export default function RootLayout() {
|
||||
...FontAwesome.font,
|
||||
});
|
||||
const [i18nReady, setI18nReady] = useState(false);
|
||||
const [appReady, setAppReady] = useState(false);
|
||||
const [splashOverlayVisible, setSplashOverlayVisible] = useState(true);
|
||||
const splashOpacity = useRef(new Animated.Value(1)).current;
|
||||
const hasHiddenNativeSplashRef = useRef(false);
|
||||
|
||||
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
|
||||
useEffect(() => {
|
||||
@@ -68,17 +72,52 @@ export default function RootLayout() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// 等字体与 i18n 都准备好后再隐藏启动页,避免文案闪烁
|
||||
if (loaded && i18nReady) {
|
||||
SplashScreen.hideAsync();
|
||||
}
|
||||
// 字体与 i18n 都准备好后,允许渲染 App(原生 splash 的隐藏交给 onLayout,避免“硬切/闪白”)
|
||||
if (loaded && i18nReady) setAppReady(true);
|
||||
}, [loaded, i18nReady]);
|
||||
|
||||
const onLayoutRootView = useCallback(() => {
|
||||
if (!appReady) return;
|
||||
if (hasHiddenNativeSplashRef.current) return;
|
||||
hasHiddenNativeSplashRef.current = true;
|
||||
|
||||
// 先隐藏原生 splash,再把同款覆盖层淡出,视觉上实现平滑过渡
|
||||
void SplashScreen.hideAsync().finally(() => {
|
||||
Animated.timing(splashOpacity, {
|
||||
toValue: 0,
|
||||
duration: 380,
|
||||
useNativeDriver: true,
|
||||
}).start(({ finished }) => {
|
||||
if (finished) setSplashOverlayVisible(false);
|
||||
});
|
||||
});
|
||||
}, [appReady, splashOpacity]);
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (!appReady) return null;
|
||||
return <RootLayoutNav />;
|
||||
}, [appReady]);
|
||||
|
||||
if (!loaded || !i18nReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <RootLayoutNav />;
|
||||
return (
|
||||
<View style={styles.root} onLayout={onLayoutRootView}>
|
||||
{content}
|
||||
{splashOverlayVisible && (
|
||||
<Animated.View pointerEvents="none" style={[StyleSheet.absoluteFill, { opacity: splashOpacity }]}>
|
||||
<View style={styles.splashOverlay}>
|
||||
<Image
|
||||
source={require('../assets/images/splashScreen.png')}
|
||||
style={styles.splashImage}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
</Animated.View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function RootLayoutNav() {
|
||||
@@ -118,3 +157,20 @@ function RootLayoutNav() {
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
splashOverlay: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
// 与 app.json 的 expo.splash.backgroundColor 保持一致
|
||||
backgroundColor: '#EAD2BA',
|
||||
},
|
||||
splashImage: {
|
||||
width: '80%',
|
||||
height: '80%',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user