diff --git a/app.config.js b/app.config.js
index 6e43ab83f8..c2eab958ea 100644
--- a/app.config.js
+++ b/app.config.js
@@ -310,15 +310,17 @@ 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: {
+ ios: {
enableFullScreenImage_legacy: true, // iOS only
- backgroundColor: '#446DA9', // based on illustration
- image: './assets/splash/splash-mobile-dark.png',
+ backgroundColor: '#A8CCFF', // primary_200
+ image: './assets/splash/splash.png',
resizeMode: 'cover',
+ dark: {
+ enableFullScreenImage_legacy: true, // iOS only
+ backgroundColor: '#00398A', // primary_800
+ image: './assets/splash/splash-dark.png',
+ resizeMode: 'cover',
+ },
},
android: {
backgroundColor: '#A8CCFF', // primary_200
diff --git a/assets/splash/splash-mobile-dark.png b/assets/splash/illustration-mobile-dark.png
similarity index 100%
rename from assets/splash/splash-mobile-dark.png
rename to assets/splash/illustration-mobile-dark.png
diff --git a/assets/splash/splash-mobile.png b/assets/splash/illustration-mobile.png
similarity index 100%
rename from assets/splash/splash-mobile.png
rename to assets/splash/illustration-mobile.png
diff --git a/assets/splash/splash-dark.png b/assets/splash/splash-dark.png
new file mode 100644
index 0000000000..95176b3ff4
Binary files /dev/null and b/assets/splash/splash-dark.png differ
diff --git a/assets/splash/splash.png b/assets/splash/splash.png
new file mode 100644
index 0000000000..05a7392b37
Binary files /dev/null and b/assets/splash/splash.png differ
diff --git a/src/Splash.tsx b/src/Splash.tsx
index 7fc09d7ab9..7a99d36891 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/splash.png'
// @ts-ignore
-import darkSplashImagePointer from '../assets/splash/splash-mobile-dark.png'
+import darkSplashImagePointer from '../assets/splash/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 && (
+
+
+
+
+
+ )}
>
)}
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
index 77973ed3d7..cc44776969 100644
--- a/src/view/com/auth/SplashScreen.tsx
+++ b/src/view/com/auth/SplashScreen.tsx
@@ -11,9 +11,9 @@ import {Logotype} from '#/view/icons/Logotype'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
// @ts-ignore
-import splashImagePointer from '../../../../assets/splash/splash-mobile.png'
+import splashImagePointer from '../../../../assets/splash/illustration-mobile.png'
// @ts-ignore
-import darkSplashImagePointer from '../../../../assets/splash/splash-mobile-dark.png'
+import darkSplashImagePointer from '../../../../assets/splash/illustration-mobile-dark.png'
const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri
const darkSplashImageUri = RNImage.resolveAssetSource(
darkSplashImagePointer,
@@ -78,7 +78,7 @@ export const SplashScreen = ({
+ style={[a.px_5xl, a.gap_md, a.pb_sm]}>