Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions AnimatedTabBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, { useRef } from 'react';
import { View, TouchableOpacity, Animated, Text } from 'react-native';
import Ionicons from '@expo/vector-icons/Ionicons';
import { colors } from './constants/colors';
import { styles } from './AnimatedTabBar.styles';

const TAB_ICONS = {
MainPage: 'home',
AccessStack: 'list',
MyPageStack: 'person-sharp',
AlertStack: 'notifications',
};

const TAB_LABELS = {
MainPage: 'ν™ˆ',
AccessStack: 'μΆœμž… κΆŒν•œ',
MyPageStack: 'λ§ˆμ΄νŽ˜μ΄μ§€',
AlertStack: 'μ•Œλ¦Ό',
};

export default function AnimatedTabBar({ state, descriptors, navigation }) {
const scales = useRef(state.routes.map(() => new Animated.Value(1))).current;
const tilts = useRef(state.routes.map(() => new Animated.Value(0))).current;

const onPress = (route, index, isFocused) => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
// 포컀슀된 탭이 μ•„λ‹ˆκ³ , μ΄λ²€νŠΈκ°€ μ·¨μ†Œλ˜μ§€ μ•Šμ•˜μœΌλ©΄ ν•΄λ‹Ή route둜 이동
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
Animated.parallel([
// 크기 λ³€ν™”
Animated.sequence([
Animated.timing(scales[index], { toValue: 1.2, duration: 120, useNativeDriver: true }),
Animated.spring(scales[index], { toValue: 1, friction: 3, useNativeDriver: true }),
]),
// 기울기 λ³€ν™”
Animated.sequence([
Animated.timing(tilts[index], { toValue: 1, duration: 150, useNativeDriver: true }), // 느리게(150ms)
Animated.timing(tilts[index], { toValue: -1, duration: 150, useNativeDriver: true }), // 느리게(150ms)
Animated.timing(tilts[index], { toValue: 0, duration: 100, useNativeDriver: true }), // μ€‘μ•™μœΌλ‘œ 볡귀
]),
]).start();
};

return (
<View style={styles.tabBar}>
{state.routes.map((route, index) => {
// ν˜„μž¬ 탭이 포컀슀된 μƒνƒœμΈμ§€ 확인
const isFocused = state.index === index;
// ν˜„μž¬ νƒ­μ˜ μ•„μ΄μ½˜κ³Ό λ ˆμ΄λΈ” μ„€μ •
const iconName = TAB_ICONS[route.name] || 'ellipse-outline';
const label = TAB_LABELS[route.name] || route.name;

return (
<TouchableOpacity
key={route.key}
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
onPress={() => onPress(route, index, isFocused)}
style={styles.tab}
activeOpacity={0.8}
>
<Animated.View
style={[
styles.iconWrapper,
isFocused && styles.iconWrapperActive,
{
transform: [
{ scale: scales[index] },
{
rotate: tilts[index].interpolate({
inputRange: [-1, 0, 1],
outputRange: ['-8deg', '0deg', '8deg'],
}),
},
],
},
]}
>
<Ionicons
name={iconName}
size={25}
style={isFocused ? styles.iconActive : styles.icon}
/>
</Animated.View>
<Text style={[styles.label, isFocused && styles.labelActive]}>{label}</Text>
</TouchableOpacity>
);
})}
</View>
);
}
50 changes: 50 additions & 0 deletions AnimatedTabBar.styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { StyleSheet } from 'react-native';
import { colors } from './constants/colors';

export const styles = StyleSheet.create({
tabBar: {
flexDirection: 'row',
height: 80, // ν•˜λ‹¨ νƒ­ 높이
backgroundColor: colors.white, // ν•˜λ‹¨ νƒ­ 배경색
// ν…Œλ‘λ¦¬ 반경
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
// 그림자 효과
shadowColor: colors.black,
elevation: 8,
},
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
iconWrapper: {
backgroundColor: colors.white,
padding: 3,
borderRadius: 16,
marginBottom: 2,
alignItems: 'center',
justifyContent: 'center',
},
iconWrapperActive: {
backgroundColor: colors.white,
padding: 3,
borderRadius: 8,
},
icon: {
color: colors.lightGray,
},
iconActive: {
color: colors.primary,
},
label: {
fontSize: 13,
color: colors.lightGray,
fontWeight: 'bold',
marginTop: 2,
},
labelActive: {
color: colors.darkGray,
fontWeight: 'bold',
},
});
8 changes: 7 additions & 1 deletion App.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import AppNavigator from './AppNavigator';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';

const App = () => {
return <AppNavigator />;
return (
<SafeAreaProvider>
<AppNavigator />
<SafeAreaView edges={['bottom']} />
</SafeAreaProvider>
);
};

export default App;
Loading