import React, {useCallback, useEffect} from 'react' import {View, StyleSheet, Image as RNImage} from 'react-native' import * as SplashScreen from 'expo-splash-screen' import {Image} from 'expo-image' import {platformApiLevel} from 'expo-device' import Animated, { interpolate, runOnJS, useAnimatedStyle, useSharedValue, withTiming, Easing, } from 'react-native-reanimated' import MaskedView from '@react-native-masked-view/masked-view' import {useSafeAreaInsets} from 'react-native-safe-area-context' import Svg, {Path, SvgProps} from 'react-native-svg' // @ts-ignore import splashImagePointer from '../assets/splash.png' const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) { const width = 1000 const height = width * (67 / 64) return ( ) }) type Props = { isReady: boolean } const AnimatedLogo = Animated.createAnimatedComponent(Logo) export function Splash(props: React.PropsWithChildren) { const insets = useSafeAreaInsets() const intro = useSharedValue(0) const outroLogo = useSharedValue(0) const outroApp = useSharedValue(0) const outroAppOpacity = useSharedValue(0) const [isAnimationComplete, setIsAnimationComplete] = React.useState(false) const [isImageLoaded, setIsImageLoaded] = React.useState(false) const isReady = props.isReady && isImageLoaded const logoAnimations = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'), }, { scale: interpolate( outroLogo.value, [0, 0.08, 1], [1, 0.8, 400], 'clamp', ), }, ], opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), } }) const appAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(outroApp.value, [0, 1], [1.1, 1], 'clamp'), }, ], opacity: interpolate( outroAppOpacity.value, [0, 0.08, 0.15, 1], [0, 0, 1, 1], 'clamp', ), } }) const onFinish = useCallback(() => setIsAnimationComplete(true), []) useEffect(() => { if (isReady) { // hide on mount SplashScreen.hideAsync().catch(() => {}) intro.value = withTiming( 1, {duration: 400, easing: Easing.out(Easing.cubic)}, async () => { // set these values to check animation at specific point // outroLogo.value = 0.1 // outroApp.value = 0.1 outroLogo.value = withTiming( 1, {duration: 1200, easing: Easing.in(Easing.cubic)}, () => { runOnJS(onFinish)() }, ) outroApp.value = withTiming(1, { duration: 1200, easing: Easing.inOut(Easing.cubic), }) outroAppOpacity.value = withTiming(1, { duration: 1200, easing: Easing.in(Easing.cubic), }) }, ) } }, [onFinish, intro, outroLogo, outroApp, outroAppOpacity, isReady]) const onLoadEnd = useCallback(() => { setIsImageLoaded(true) }, [setIsImageLoaded]) return ( {!isAnimationComplete && ( )} {platformApiLevel && platformApiLevel <= 25 ? ( // Use a simple fade on older versions of android (work around a bug) <> {!isAnimationComplete && ( )} {props.children} ) : ( }> {!isAnimationComplete && ( )} {props.children} )} ) }