Refine animation
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 360 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 354 KiB |
+1
-1
@@ -55,7 +55,7 @@ function InnerApp() {
|
|||||||
}, [resumeSession])
|
}, [resumeSession])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Splash isReady={isInitialLoad}>
|
<Splash isReady={!isInitialLoad}>
|
||||||
<React.Fragment
|
<React.Fragment
|
||||||
// Resets the entire tree below when it changes:
|
// Resets the entire tree below when it changes:
|
||||||
key={currentAccount?.did}>
|
key={currentAccount?.did}>
|
||||||
|
|||||||
+81
-67
@@ -1,8 +1,6 @@
|
|||||||
import React, {useCallback, useEffect} from 'react'
|
import React, {useCallback, useEffect} from 'react'
|
||||||
import {View, Image, StyleSheet} from 'react-native'
|
import {View, StyleSheet} from 'react-native'
|
||||||
import * as SplashScreen from 'expo-splash-screen'
|
import * as SplashScreen from 'expo-splash-screen'
|
||||||
// @ts-ignore
|
|
||||||
import splashImagePointer from '../assets/icon-android-foreground.png'
|
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import Animated, {
|
import Animated, {
|
||||||
interpolate,
|
interpolate,
|
||||||
@@ -10,10 +8,10 @@ import Animated, {
|
|||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming,
|
withTiming,
|
||||||
|
Easing,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import MaskedView from '@react-native-masked-view/masked-view'
|
import MaskedView from '@react-native-masked-view/masked-view'
|
||||||
|
import {Logo} from '#/view/icons/Logo'
|
||||||
const splashImageUri = Image.resolveAssetSource(splashImagePointer).uri
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isReady: boolean
|
isReady: boolean
|
||||||
@@ -22,34 +20,18 @@ type Props = {
|
|||||||
SplashScreen.preventAutoHideAsync().catch(() => {})
|
SplashScreen.preventAutoHideAsync().catch(() => {})
|
||||||
|
|
||||||
export function Splash(props: React.PropsWithChildren<Props>) {
|
export function Splash(props: React.PropsWithChildren<Props>) {
|
||||||
const progress = useSharedValue(0)
|
const fadeAnimationProgress = useSharedValue(0)
|
||||||
|
const scaleAnimationProgress = useSharedValue(0)
|
||||||
const [isAnimationComplete, setIsAnimationComplete] = React.useState(false)
|
const [isAnimationComplete, setIsAnimationComplete] = React.useState(false)
|
||||||
|
|
||||||
const onFinish = useCallback(() => {
|
const logoScaleAnimation = useAnimatedStyle(() => {
|
||||||
SplashScreen.hideAsync().catch(() => {})
|
|
||||||
setIsAnimationComplete(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const onLoadEnd = React.useCallback(() => {
|
|
||||||
SplashScreen.hideAsync().catch(() => {})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.isReady) {
|
|
||||||
progress.value = withTiming(1, {duration: 1000}, () => {
|
|
||||||
runOnJS(onFinish)()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [onFinish, progress, props.isReady])
|
|
||||||
|
|
||||||
const imageScale = useAnimatedStyle(() => {
|
|
||||||
return {
|
return {
|
||||||
transform: [
|
transform: [
|
||||||
{
|
{
|
||||||
scale: interpolate(
|
scale: interpolate(
|
||||||
progress.value,
|
scaleAnimationProgress.value,
|
||||||
[0, 0.2, 1],
|
[0, 0.2, 0.22, 1],
|
||||||
[1, 0.8, 100],
|
[1, 0.8, 0.8, 200],
|
||||||
'clamp',
|
'clamp',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -57,72 +39,104 @@ export function Splash(props: React.PropsWithChildren<Props>) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const appScale = useAnimatedStyle(() => {
|
const logoFadeInAnimation = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
opacity: interpolate(
|
||||||
|
fadeAnimationProgress.value,
|
||||||
|
[0, 0.5, 1],
|
||||||
|
[0, 1, 1],
|
||||||
|
'clamp',
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const appScaleAnimation = useAnimatedStyle(() => {
|
||||||
return {
|
return {
|
||||||
transform: [
|
transform: [
|
||||||
{
|
{
|
||||||
scale: interpolate(progress.value, [0, 1], [1.1, 1]),
|
scale: interpolate(
|
||||||
|
scaleAnimationProgress.value,
|
||||||
|
[0, 0.22, 1],
|
||||||
|
[1.05, 1.05, 1],
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const opacityScale = useAnimatedStyle(() => {
|
// const opacityScale = { opacity: 1 }
|
||||||
|
const appFadeInAnimation = useAnimatedStyle(() => {
|
||||||
return {
|
return {
|
||||||
opacity: interpolate(progress.value, [0, 0.15, 0.3], [0, 0, 1], 'clamp'),
|
opacity: interpolate(
|
||||||
|
scaleAnimationProgress.value,
|
||||||
|
[0, 0.22, 1],
|
||||||
|
[0, 0, 1],
|
||||||
|
'clamp',
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onFinish = useCallback(() => setIsAnimationComplete(true), [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// hide on mount
|
||||||
|
SplashScreen.hideAsync().catch(() => {})
|
||||||
|
|
||||||
|
if (props.isReady) {
|
||||||
|
fadeAnimationProgress.value = withTiming(
|
||||||
|
1,
|
||||||
|
{duration: 800, easing: Easing.inOut(Easing.cubic)},
|
||||||
|
() => {
|
||||||
|
scaleAnimationProgress.value = withTiming(
|
||||||
|
1,
|
||||||
|
{duration: 1000, easing: Easing.inOut(Easing.cubic)},
|
||||||
|
() => {
|
||||||
|
runOnJS(onFinish)()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [onFinish, fadeAnimationProgress, scaleAnimationProgress, props.isReady])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={{flex: 1}}>
|
||||||
{isAnimationComplete ? null : (
|
{!isAnimationComplete && (
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={['#0A7AFF', '#59B9FF']}
|
colors={['#0A7AFF', '#59B9FF']}
|
||||||
style={[StyleSheet.absoluteFillObject]}
|
style={[StyleSheet.absoluteFillObject]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<MaskedView
|
<MaskedView
|
||||||
style={{flex: 1}}
|
style={{flex: 1}}
|
||||||
maskElement={
|
maskElement={
|
||||||
<View
|
<Animated.View
|
||||||
style={{
|
style={[
|
||||||
// Transparent background because mask is based off alpha channel.
|
{
|
||||||
backgroundColor: 'transparent',
|
// Transparent background because mask is based off alpha channel.
|
||||||
flex: 1,
|
backgroundColor: 'transparent',
|
||||||
justifyContent: 'center',
|
flex: 1,
|
||||||
alignItems: 'center',
|
justifyContent: 'center',
|
||||||
}}>
|
alignItems: 'center',
|
||||||
<Animated.Image
|
},
|
||||||
onLoadEnd={onLoadEnd}
|
logoScaleAnimation,
|
||||||
style={[
|
logoFadeInAnimation,
|
||||||
{
|
]}>
|
||||||
resizeMode: 'contain',
|
<Logo width={100} />
|
||||||
width: '50%',
|
</Animated.View>
|
||||||
height: '50%',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '25%',
|
|
||||||
left: '25%',
|
|
||||||
},
|
|
||||||
imageScale,
|
|
||||||
]}
|
|
||||||
source={{uri: splashImageUri}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
}>
|
}>
|
||||||
<Animated.View style={[{flex: 1}, opacityScale, appScale]}>
|
{!isAnimationComplete && (
|
||||||
|
<View
|
||||||
|
style={[StyleSheet.absoluteFillObject, {backgroundColor: 'white'}]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Animated.View
|
||||||
|
style={[{flex: 1}, appFadeInAnimation, appScaleAnimation]}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</MaskedView>
|
</MaskedView>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
import Svg, {SvgProps, Path} from 'react-native-svg'
|
|
||||||
const Logo = (props: SvgProps) => (
|
|
||||||
<Svg width={'100%'} height={'100%'} fill="none" {...props}>
|
|
||||||
<Path
|
|
||||||
fill="#fff"
|
|
||||||
d="M180 141.964c-16.301-31.702-60.692-90.782-101.965-119.92C38.497-5.868 23.414-1.032 13.526 3.436 2.081 8.608 0 26.178 0 36.516c0 10.338 5.667 84.756 9.364 97.178 12.215 41.044 55.696 54.913 95.74 50.462 2.047-.304 4.123-.584 6.225-.844-2.062.33-4.139.612-6.225.844-58.684 8.691-110.8 30.077-42.446 106.174 75.19 77.85 103.047-16.693 117.342-64.628 14.295 47.935 30.76 139.069 115.995 64.628 64.005-64.628 17.585-97.48-41.099-106.172a131.118 131.118 0 0 1-6.225-.843c2.102.259 4.178.54 6.225.843 40.044 4.452 83.525-9.418 95.74-50.461 3.697-12.422 9.364-86.84 9.364-97.178 0-10.338-2.081-27.909-13.526-33.08-9.888-4.468-24.971-9.305-64.509 18.608C240.692 51.184 196.301 110.262 180 141.964Z"
|
|
||||||
/>
|
|
||||||
</Svg>
|
|
||||||
)
|
|
||||||
|
|||||||
+10
-2
@@ -10,12 +10,20 @@ import Svg, {
|
|||||||
|
|
||||||
import {colors} from '#/lib/styles'
|
import {colors} from '#/lib/styles'
|
||||||
|
|
||||||
|
const ratio = 106 / 120
|
||||||
|
|
||||||
export function Logo({fill, ...rest}: {fill?: PathProps['fill']} & SvgProps) {
|
export function Logo({fill, ...rest}: {fill?: PathProps['fill']} & SvgProps) {
|
||||||
const gradient = fill === 'sky'
|
const gradient = fill === 'sky'
|
||||||
const _fill = gradient ? 'url(#sky)' : fill || colors.blue3
|
const _fill = gradient ? 'url(#sky)' : fill || colors.blue3
|
||||||
const size = rest.width || 32
|
// @ts-ignore it's fiiiiine
|
||||||
|
const size = parseInt(rest.width || 32)
|
||||||
return (
|
return (
|
||||||
<Svg fill="none" viewBox="0 0 64 64" {...rest} width={size} height={size}>
|
<Svg
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
{...rest}
|
||||||
|
width={size}
|
||||||
|
height={size * ratio}>
|
||||||
{gradient && (
|
{gradient && (
|
||||||
<Defs>
|
<Defs>
|
||||||
<LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
<LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
|||||||
Reference in New Issue
Block a user