about summary refs log tree commit diff
path: root/src/Splash.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2023-12-14 14:48:40 -0600
committerGitHub <noreply@github.com>2023-12-14 14:48:40 -0600
commit7897dd24a14e65d8ab1aa0e474a09e0628da31b7 (patch)
tree77cec13da7f55ae1d8535550f497ad7314ece0e4 /src/Splash.tsx
parent075ffdf583c5393b896d22dd179d03208311ef4a (diff)
downloadvoidsky-7897dd24a14e65d8ab1aa0e474a09e0628da31b7.tar.zst
🤫 (#2211)
* Add new assets

* Add splashiness

* Add butter icon, spread it

* Cream together eggs, sugar, and vanilla

* Hi, I'd like to place and order. Yeah, none pizza with left beef, plz.

* test

* Refine animation

* tweak

* tweak

* tweak

* Tweak

* Simplify

* Cleanup

* Fix android logo

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
Diffstat (limited to 'src/Splash.tsx')
-rw-r--r--src/Splash.tsx154
1 files changed, 154 insertions, 0 deletions
diff --git a/src/Splash.tsx b/src/Splash.tsx
new file mode 100644
index 000000000..92ed366f9
--- /dev/null
+++ b/src/Splash.tsx
@@ -0,0 +1,154 @@
+import React, {useCallback, useEffect} from 'react'
+import {View, StyleSheet} from 'react-native'
+import * as SplashScreen from 'expo-splash-screen'
+import LinearGradient from 'react-native-linear-gradient'
+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'
+
+export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) {
+  const width = 1000
+  const height = width * (67 / 64)
+  return (
+    <Svg
+      fill="none"
+      // @ts-ignore it's fiiiiine
+      ref={ref}
+      viewBox="0 0 64 66"
+      style={{width, height}}>
+      <Path
+        fill="#fff"
+        d="M13.873 3.77C21.21 9.243 29.103 20.342 32 26.3v15.732c0-.335-.13.043-.41.858-1.512 4.414-7.418 21.642-20.923 7.87-7.111-7.252-3.819-14.503 9.125-16.692-7.405 1.252-15.73-.817-18.014-8.93C1.12 22.804 0 8.431 0 6.488 0-3.237 8.579-.18 13.873 3.77ZM50.127 3.77C42.79 9.243 34.897 20.342 32 26.3v15.732c0-.335.13.043.41.858 1.512 4.414 7.418 21.642 20.923 7.87 7.111-7.252 3.819-14.503-9.125-16.692 7.405 1.252 15.73-.817 18.014-8.93C62.88 22.804 64 8.431 64 6.488 64-3.237 55.422-.18 50.127 3.77Z"
+      />
+    </Svg>
+  )
+})
+
+type Props = {
+  isReady: boolean
+}
+
+SplashScreen.preventAutoHideAsync().catch(() => {})
+
+const AnimatedLogo = Animated.createAnimatedComponent(Logo)
+
+export function Splash(props: React.PropsWithChildren<Props>) {
+  const insets = useSafeAreaInsets()
+  const intro = useSharedValue(0)
+  const outroLogo = useSharedValue(0)
+  const outroApp = useSharedValue(0)
+  const [isAnimationComplete, setIsAnimationComplete] = React.useState(false)
+
+  const logoAnimations = useAnimatedStyle(() => {
+    return {
+      transform: [
+        {
+          scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'),
+        },
+        {
+          scale: interpolate(
+            outroLogo.value,
+            [0, 0.06, 0.08, 1],
+            [1, 0.8, 0.8, 800],
+            'clamp',
+          ),
+        },
+      ],
+      opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'),
+    }
+  })
+
+  const appAnimation = useAnimatedStyle(() => {
+    return {
+      transform: [
+        {
+          scale: interpolate(
+            outroApp.value,
+            [0, 0.7, 1],
+            [1.1, 1.1, 1],
+            'clamp',
+          ),
+        },
+      ],
+      opacity: interpolate(outroApp.value, [0, 0.7, 1], [0, 0, 1], 'clamp'),
+    }
+  })
+
+  const onFinish = useCallback(() => setIsAnimationComplete(true), [])
+
+  useEffect(() => {
+    if (props.isReady) {
+      // hide on mount
+      SplashScreen.hideAsync().catch(() => {})
+
+      intro.value = withTiming(
+        1,
+        {duration: 200, easing: Easing.out(Easing.cubic)},
+        async () => {
+          outroLogo.value = withTiming(
+            1,
+            {duration: 1200, easing: Easing.in(Easing.cubic)},
+            () => {
+              runOnJS(onFinish)()
+            },
+          )
+          outroApp.value = withTiming(
+            1,
+            {duration: 1200, easing: Easing.inOut(Easing.cubic)},
+            () => {
+              runOnJS(onFinish)()
+            },
+          )
+        },
+      )
+    }
+  }, [onFinish, intro, outroLogo, outroApp, props.isReady])
+
+  return (
+    <View style={{flex: 1}}>
+      {!isAnimationComplete && (
+        <LinearGradient
+          colors={['#0A7AFF', '#59B9FF']}
+          style={[StyleSheet.absoluteFillObject]}
+        />
+      )}
+
+      <MaskedView
+        style={[StyleSheet.absoluteFillObject]}
+        maskElement={
+          <Animated.View
+            style={[
+              StyleSheet.absoluteFillObject,
+              {
+                // Transparent background because mask is based off alpha channel.
+                backgroundColor: 'transparent',
+                flex: 1,
+                justifyContent: 'center',
+                alignItems: 'center',
+                transform: [{translateY: -(insets.top / 2)}, {scale: 0.1}], // scale from 1000px to 100px
+              },
+            ]}>
+            <AnimatedLogo style={[logoAnimations]} />
+          </Animated.View>
+        }>
+        {!isAnimationComplete && (
+          <View
+            style={[StyleSheet.absoluteFillObject, {backgroundColor: 'white'}]}
+          />
+        )}
+
+        <Animated.View style={[{flex: 1}, appAnimation]}>
+          {props.children}
+        </Animated.View>
+      </MaskedView>
+    </View>
+  )
+}