728x90
반응형
앱을 만드는 중에, 바텀탭버튼에 애니메이션 효과를 부탁받았다.
늘 Lottie파일로 구현했는데, 이번에는
react-native-svg 로 아이콘을 보이고, 감싸는 view에 애니메이션 효과를 주면서 해결했다. 코드는
// import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import {
BottomTabBarButtonProps,
createBottomTabNavigator,
} from '@react-navigation/bottom-tabs';
import { StackScreenProps } from '@react-navigation/stack';
import React, { useEffect, useRef } from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import SplashScreen from 'react-native-splash-screen';
import CommunityOff from '../../assets/icons/bottomtab/communityOff.svg';
import CommunityOn from '../../assets/icons/bottomtab/communityOn.svg';
import HomeOff from '../../assets/icons/bottomtab/homeOff.svg';
import HomeOn from '../../assets/icons/bottomtab/homeOn.svg';
import MyOff from '../../assets/icons/bottomtab/myOff.svg';
import MyOn from '../../assets/icons/bottomtab/myOn.svg';
import SupportOff from '../../assets/icons/bottomtab/supportOff.svg';
import SupportOn from '../../assets/icons/bottomtab/supportOn.svg';
import TvOff from '../../assets/icons/bottomtab/tvOff.svg';
import TvOn from '../../assets/icons/bottomtab/tvOn.svg';
import { WowplanetHeader } from '../../components/WowplanetHeader';
import { RootStackParamList } from '../../navigation/RootNavigator';
import { CommunityScreen } from '../CommunityScreen';
import { HomeScreen } from '../HomeScreen';
import { MyScreen } from '../MyScreen';
import { SupportScreen } from '../SupportScreen';
import { TvScreen } from '../TvScreen';
import * as Animatable from 'react-native-animatable';
import { heightScale } from '../../util/Layout';
export type BottomTabParamList = {
HomeScreen: undefined;
SupportScreen: { userId: string };
CommunityScreen: { sort: 'latest' | 'top' } | undefined;
TvScreen: { sort: 'latest' | 'top' } | undefined;
MyScreen: undefined;
};
const TabBarIcon = ({ name, focused }: any) => {
console.log('route ::', name);
switch (name) {
case 'SupportScreen':
return (
<View
style={[
{
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
margin: 0,
},
]}
>
{focused ? (
<SupportOn width={35} height={35} />
) : (
<SupportOff width={35} height={35} />
)}
</View>
);
case 'CommunityScreen':
return (
<View
style={{
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
}}
>
{focused ? (
<CommunityOn width={35} height={35} />
) : (
<CommunityOff width={35} height={35} />
)}
</View>
);
case 'HomeScreen':
return (
<View
style={{
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
}}
>
{focused ? (
<HomeOn width={35} height={35} />
) : (
<HomeOff width={35} height={35} />
)}
</View>
);
case 'TvScreen':
return (
<View
style={{
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
}}
>
{focused ? (
<TvOn width={35} height={35} />
) : (
<TvOff width={35} height={35} />
)}
</View>
);
case 'MyScreen':
return (
<View
style={{
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
}}
>
{focused ? (
<MyOn width={35} height={35} />
) : (
<MyOff width={35} height={35} />
)}
</View>
);
}
};
const TabArr = [
{
route: 'SupportScreen',
label: 'SupportScreen',
component: SupportScreen,
},
{
route: 'CommunityScreen',
label: 'CommunityScreen',
component: CommunityScreen,
},
{
route: 'HomeScreen',
label: 'HomeScreen',
component: HomeScreen,
},
{
route: 'TvScreen',
label: 'TvScreen',
component: TvScreen,
},
{
route: 'MyScreen',
label: 'MyScreen',
component: MyScreen,
},
];
const TabButton = (props: BottomTabBarButtonProps) => {
const { item, onPress, accessibilityState } = props;
const focused = accessibilityState.selected;
const viewRef = useRef(null);
console.log('item :::', item);
useEffect(() => {
if (focused) {
viewRef.current.animate({
0: { scale: 0.5, rotate: '360deg' },
1: { scale: 1.0, rotate: '0deg' },
});
} else {
viewRef.current.animate({
0: { scale: 1.0, rotate: '-360deg' },
1: { scale: 1, rotate: '0deg' },
});
}
}, [focused]);
return (
<TouchableOpacity
onPress={onPress}
activeOpacity={1}
style={styles.container}
>
<Animatable.View ref={viewRef} duration={1000} style={styles.container}>
<TabBarIcon focused={focused} name={item.route} />
</Animatable.View>
</TouchableOpacity>
);
};
export const BottomTabNavigator = (
props: StackScreenProps<RootStackParamList, 'BottomTab'>,
) => {
const Tab = createBottomTabNavigator<BottomTabParamList>();
console.log('rendering BottomTabNavigator :::::::::::');
useEffect(() => {
SplashScreen.hide();
}, []);
return (
<View style={{ flex: 1 }}>
<WowplanetHeader navigation={props.navigation} route={props.route} />
<Tab.Navigator
screenOptions={() => ({
headerShown: false,
tabBarShowLabel: false,
lazy: true,
tabBarStyle: {
height: heightScale(75),
},
})}
screenListeners={({ route }) => ({
tabPress: () => {
console.log(route.name);
},
})}
initialRouteName="HomeScreen"
>
{TabArr.map((item, index) => {
return (
<Tab.Screen
key={index}
name={item.route}
component={item.component}
options={{
tabBarShowLabel: false,
tabBarButton: (props) => <TabButton {...props} item={item} />,
}}
/>
);
})}
</Tab.Navigator>
</View>
);
};
728x90
반응형