diff --git a/app.config.js b/app.config.js index 6e43ab83f8..7528e9010c 100644 --- a/app.config.js +++ b/app.config.js @@ -310,24 +310,26 @@ module.exports = function (_config) { [ 'expo-splash-screen', { - enableFullScreenImage_legacy: true, // iOS only - backgroundColor: '#EEF5FF', // based on illustration - image: './assets/splash/splash-mobile.png', - resizeMode: 'cover', - dark: { - enableFullScreenImage_legacy: true, // iOS only - backgroundColor: '#446DA9', // based on illustration - image: './assets/splash/splash-mobile-dark.png', + ios: { + enableFullScreenImage_legacy: true, + backgroundColor: '#ffffff', + image: './assets/splash.png', resizeMode: 'cover', + dark: { + enableFullScreenImage_legacy: true, + backgroundColor: '#001429', + image: './assets/splash-dark.png', + resizeMode: 'cover', + }, }, android: { - backgroundColor: '#A8CCFF', // primary_200 - image: './assets/splash/android-splash-logo-white.png', - imageWidth: 105, + backgroundColor: '#0c7cff', + image: './assets/splash-android-icon.png', + imageWidth: 150, dark: { - backgroundColor: '#00398A', // primary_800 - image: './assets/splash/android-splash-logo-white.png', - imageWidth: 105, + backgroundColor: '#0c2a49', + image: './assets/splash-android-icon-dark.png', + imageWidth: 150, }, }, }, diff --git a/assets/splash-android-icon-dark.png b/assets/splash-android-icon-dark.png new file mode 100644 index 0000000000..c19a01dc6d Binary files /dev/null and b/assets/splash-android-icon-dark.png differ diff --git a/assets/splash-android-icon.png b/assets/splash-android-icon.png new file mode 100644 index 0000000000..280386c900 Binary files /dev/null and b/assets/splash-android-icon.png differ diff --git a/assets/splash-dark.png b/assets/splash-dark.png new file mode 100644 index 0000000000..95176b3ff4 Binary files /dev/null and b/assets/splash-dark.png differ diff --git a/assets/splash.png b/assets/splash.png new file mode 100644 index 0000000000..05a7392b37 Binary files /dev/null and b/assets/splash.png differ diff --git a/assets/splash/android-splash-logo-white.png b/assets/splash/android-splash-logo-white.png deleted file mode 100644 index b2b570b933..0000000000 Binary files a/assets/splash/android-splash-logo-white.png and /dev/null differ diff --git a/src/Splash.tsx b/src/Splash.tsx index 7fc09d7ab9..47e70b375f 100644 --- a/src/Splash.tsx +++ b/src/Splash.tsx @@ -9,30 +9,55 @@ import { import Animated, { Easing, interpolate, + runOnJS, useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import Svg, {Path, type SvgProps} from 'react-native-svg' import {Image} from 'expo-image' import * as SplashScreen from 'expo-splash-screen' +import {Logotype} from '#/view/icons/Logotype' // @ts-ignore -import splashImagePointer from '../assets/splash/splash-mobile.png' +import splashImagePointer from '../assets/splash.png' // @ts-ignore -import darkSplashImagePointer from '../assets/splash/splash-mobile-dark.png' +import darkSplashImagePointer from '../assets/splash-dark.png' const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri const darkSplashImageUri = RNImage.resolveAssetSource( darkSplashImagePointer, ).uri +export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) { + const width = 1000 + const height = width * (67 / 64) + return ( + + + + ) +}) + type Props = { isReady: boolean } export function Splash(props: React.PropsWithChildren) { 'use no memo' + const insets = useSafeAreaInsets() + const intro = useSharedValue(0) + const outroLogo = useSharedValue(0) + const outroApp = useSharedValue(0) const outroAppOpacity = useSharedValue(0) - const colorScheme = useColorScheme() const [isAnimationComplete, setIsAnimationComplete] = React.useState(false) const [isImageLoaded, setIsImageLoaded] = React.useState(false) const [isLayoutReady, setIsLayoutReady] = React.useState(false) @@ -44,10 +69,62 @@ export function Splash(props: React.PropsWithChildren) { isImageLoaded && isLayoutReady && reduceMotion !== undefined + + const colorScheme = useColorScheme() const isDarkMode = colorScheme === 'dark' + const logoAnimation = useAnimatedStyle(() => { + return { + transform: [ + { + scale: interpolate(intro.get(), [0, 1], [0.8, 1], 'clamp'), + }, + { + scale: interpolate( + outroLogo.get(), + [0, 0.08, 1], + [1, 0.8, 500], + 'clamp', + ), + }, + ], + opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), + } + }) + const bottomLogoAnimation = useAnimatedStyle(() => { + return { + opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), + } + }) + const reducedLogoAnimation = useAnimatedStyle(() => { + return { + transform: [ + { + scale: interpolate(intro.get(), [0, 1], [0.8, 1], 'clamp'), + }, + ], + opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), + } + }) + + const logoWrapperAnimation = useAnimatedStyle(() => { + return { + opacity: interpolate( + outroAppOpacity.get(), + [0, 0.1, 0.2, 1], + [1, 1, 0, 0], + 'clamp', + ), + } + }) + const appAnimation = useAnimatedStyle(() => { return { + transform: [ + { + scale: interpolate(outroApp.get(), [0, 1], [1.1, 1], 'clamp'), + }, + ], opacity: interpolate( outroAppOpacity.get(), [0, 0.1, 0.2, 1], @@ -65,21 +142,50 @@ export function Splash(props: React.PropsWithChildren) { if (isReady) { SplashScreen.hideAsync() .then(() => { - outroAppOpacity.set(() => - withTiming(1, { - duration: 1200, - easing: Easing.in(Easing.cubic), - }), + intro.set(() => + withTiming( + 1, + {duration: 400, easing: Easing.out(Easing.cubic)}, + async () => { + // set these values to check animation at specific point + outroLogo.set(() => + withTiming( + 1, + {duration: 1200, easing: Easing.in(Easing.cubic)}, + () => { + runOnJS(onFinish)() + }, + ), + ) + outroApp.set(() => + withTiming(1, { + duration: 1200, + easing: Easing.inOut(Easing.cubic), + }), + ) + outroAppOpacity.set(() => + withTiming(1, { + duration: 1200, + easing: Easing.in(Easing.cubic), + }), + ) + }, + ), ) }) .catch(() => {}) } - }, [onFinish, outroAppOpacity, isReady]) + }, [onFinish, intro, outroLogo, outroApp, outroAppOpacity, isReady]) useEffect(() => { AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion) }, []) + const logoAnimations = + reduceMotion === true ? reducedLogoAnimation : logoAnimation + // special off-spec color for dark mode + const logoBg = isDarkMode ? '#0F1824' : '#fff' + return ( {!isAnimationComplete && ( @@ -90,6 +196,22 @@ export function Splash(props: React.PropsWithChildren) { source={{uri: isDarkMode ? darkSplashImageUri : splashImageUri}} style={StyleSheet.absoluteFillObject} /> + + + + )} @@ -98,6 +220,24 @@ export function Splash(props: React.PropsWithChildren) { {props.children} + + {!isAnimationComplete && ( + + + + + + )} )}