fix(app-expo): use brand splash icon on logout replay overlay

Replace Expo template logo and blue gradient with splash-icon.png and

Co-authored-by: Cursor <cursoragent@cursor.com>
#E6F4FE so post-logout splash matches the native cold-start screen.
This commit is contained in:
Kevin
2026-05-22 14:19:53 +08:00
parent 53e0065e3e
commit 8f66f94a1a
3 changed files with 40 additions and 177 deletions

View File

@@ -1,6 +0,0 @@
.expoLogoBackground {
background-image: linear-gradient(180deg, #3c9ffe, #0274df);
border-radius: 40px;
width: 128px;
height: 128px;
}

View File

@@ -8,9 +8,11 @@ import { completeSplashReplay } from '@/core/splash-replay';
const INITIAL_SCALE_FACTOR = Dimensions.get('screen').height / 90;
const DURATION = 600;
/** Matches expo-splash-screen `imageWidth` in app.config.ts */
const SPLASH_LOGO_SIZE = 200;
/** Brand gate background (matches native splash / overlay). */
export const BRAND_BOOTSTRAP_BG = '#208AEF';
/** Brand gate background matches native splash / adaptiveIcon (#E6F4FE). */
export const BRAND_BOOTSTRAP_BG = '#E6F4FE';
export function AnimatedSplashOverlay() {
const [visible, setVisible] = useState(true);
@@ -61,100 +63,41 @@ export function BrandBootstrapLoading() {
);
}
const keyframe = new Keyframe({
0: {
transform: [{ scale: INITIAL_SCALE_FACTOR }],
},
100: {
transform: [{ scale: 1 }],
easing: Easing.elastic(0.7),
},
});
const logoKeyframe = new Keyframe({
0: {
transform: [{ scale: 1.3 }],
transform: [{ scale: 0.92 }],
opacity: 0,
},
40: {
transform: [{ scale: 1.3 }],
opacity: 0,
easing: Easing.elastic(0.7),
},
100: {
opacity: 1,
transform: [{ scale: 1 }],
easing: Easing.elastic(0.7),
},
});
const glowKeyframe = new Keyframe({
0: {
transform: [{ rotateZ: '0deg' }],
},
100: {
transform: [{ rotateZ: '7200deg' }],
easing: Easing.out(Easing.cubic),
},
});
export function AnimatedIcon() {
return (
<View style={styles.iconContainer}>
<Animated.View
entering={glowKeyframe.duration(60 * 1000 * 4)}
style={styles.glow}
>
<Image
style={styles.glow}
source={require('@/assets/images/logo-glow.png')}
/>
</Animated.View>
<Animated.View
entering={keyframe.duration(DURATION)}
style={styles.background}
<Animated.View
style={styles.logoContainer}
entering={logoKeyframe.duration(DURATION)}
>
<Image
style={styles.splashLogo}
source={require('@/assets/images/splash-icon.png')}
contentFit="contain"
/>
<Animated.View
style={styles.imageContainer}
entering={logoKeyframe.duration(DURATION)}
>
<Image
style={styles.image}
source={require('@/assets/images/expo-logo.png')}
/>
</Animated.View>
</View>
</Animated.View>
);
}
const styles = StyleSheet.create({
imageContainer: {
logoContainer: {
justifyContent: 'center',
alignItems: 'center',
},
glow: {
width: 201,
height: 201,
position: 'absolute',
},
iconContainer: {
justifyContent: 'center',
alignItems: 'center',
width: 128,
height: 128,
zIndex: 100,
},
image: {
position: 'absolute',
width: 76,
height: 71,
},
background: {
borderRadius: 40,
experimental_backgroundImage: `linear-gradient(180deg, #3C9FFE, #0274DF)`,
width: 128,
height: 128,
position: 'absolute',
splashLogo: {
width: SPLASH_LOGO_SIZE,
height: SPLASH_LOGO_SIZE,
},
backgroundSolidColor: {
...StyleSheet.absoluteFillObject,

View File

@@ -1,12 +1,13 @@
import { Image } from 'expo-image';
import { StyleSheet, View } from 'react-native';
import Animated, { Keyframe, Easing } from 'react-native-reanimated';
import Animated, { Easing, Keyframe } from 'react-native-reanimated';
import classes from './animated-icon.module.css';
/** Matches expo-splash-screen `imageWidth` in app.config.ts */
const SPLASH_LOGO_SIZE = 200;
const DURATION = 300;
/** Brand gate background (matches native splash / overlay). */
export const BRAND_BOOTSTRAP_BG = '#208AEF';
/** Brand gate background matches native splash / adaptiveIcon (#E6F4FE). */
export const BRAND_BOOTSTRAP_BG = '#E6F4FE';
export function AnimatedSplashOverlay() {
return null;
@@ -21,81 +22,30 @@ export function BrandBootstrapLoading() {
);
}
const keyframe = new Keyframe({
0: {
transform: [{ scale: 0 }],
},
60: {
transform: [{ scale: 1.2 }],
easing: Easing.elastic(1.2),
},
100: {
transform: [{ scale: 1 }],
easing: Easing.elastic(1.2),
},
});
const logoKeyframe = new Keyframe({
0: {
transform: [{ scale: 0.92 }],
opacity: 0,
},
60: {
transform: [{ scale: 1.2 }],
opacity: 0,
easing: Easing.elastic(1.2),
},
100: {
transform: [{ scale: 1 }],
opacity: 1,
easing: Easing.elastic(1.2),
},
});
const glowKeyframe = new Keyframe({
0: {
transform: [{ rotateZ: '-180deg' }, { scale: 0.8 }],
opacity: 0,
},
[DURATION / 1000]: {
transform: [{ rotateZ: '0deg' }, { scale: 1 }],
opacity: 1,
easing: Easing.elastic(0.7),
},
100: {
transform: [{ rotateZ: '7200deg' }],
easing: Easing.out(Easing.cubic),
},
});
export function AnimatedIcon() {
return (
<View style={styles.iconContainer}>
<Animated.View
entering={glowKeyframe.duration(60 * 1000 * 4)}
style={styles.glow}
>
<Image
style={styles.glow}
source={require('@/assets/images/logo-glow.png')}
/>
</Animated.View>
<Animated.View
style={styles.background}
entering={keyframe.duration(DURATION)}
>
<div className={classes.expoLogoBackground} />
</Animated.View>
<Animated.View
style={styles.imageContainer}
entering={logoKeyframe.duration(DURATION)}
>
<Image
style={styles.image}
source={require('@/assets/images/expo-logo.png')}
/>
</Animated.View>
</View>
<Animated.View
style={styles.logoContainer}
entering={logoKeyframe.duration(DURATION)}
>
<Image
style={styles.splashLogo}
source={require('@/assets/images/splash-icon.png')}
contentFit="contain"
/>
</Animated.View>
);
}
@@ -106,36 +56,12 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
container: {
alignItems: 'center',
width: '100%',
zIndex: 1000,
position: 'absolute',
top: 128 / 2 + 138,
},
imageContainer: {
logoContainer: {
justifyContent: 'center',
alignItems: 'center',
},
glow: {
width: 201,
height: 201,
position: 'absolute',
},
iconContainer: {
justifyContent: 'center',
alignItems: 'center',
width: 128,
height: 128,
},
image: {
position: 'absolute',
width: 76,
height: 71,
},
background: {
width: 128,
height: 128,
position: 'absolute',
splashLogo: {
width: SPLASH_LOGO_SIZE,
height: SPLASH_LOGO_SIZE,
},
});