CustomTabBar.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { Colors } from "@/constants/Colors"; // Import Colors
  2. import { Images } from "@/constants/images";
  3. import { Image } from "expo-image";
  4. import { usePathname, useRouter, useSegments } from "expo-router";
  5. import React from "react";
  6. import {
  7. Dimensions,
  8. StyleSheet,
  9. Text,
  10. TouchableOpacity,
  11. View,
  12. } from "react-native";
  13. import { useSafeAreaInsets } from "react-native-safe-area-context";
  14. const { width: SCREEN_WIDTH } = Dimensions.get("window");
  15. const tabList = [
  16. {
  17. name: "大厅", // Was 首页
  18. route: "/",
  19. img: Images.tabs.home,
  20. active: Images.tabs.homeActive,
  21. },
  22. {
  23. name: "矩阵", // Was 开箱
  24. route: "/box",
  25. img: Images.tabs.box,
  26. active: Images.tabs.boxActive,
  27. },
  28. {
  29. name: "次元", // Was 福利
  30. route: "/dimension",
  31. img: Images.tabs.welfare,
  32. active: Images.tabs.welfareActive,
  33. },
  34. {
  35. name: "档案", // Was 我的
  36. route: "/mine",
  37. img: Images.tabs.mine,
  38. active: Images.tabs.mineActive,
  39. },
  40. ];
  41. export function CustomTabBar() {
  42. const router = useRouter();
  43. const segments = useSegments();
  44. const pathname = usePathname();
  45. const insets = useSafeAreaInsets();
  46. const getTabIndex = () => {
  47. // Check Box
  48. if (segments[1] === "box" || pathname?.startsWith("/box")) return 1;
  49. // Check Dimension (formerly Welfare)
  50. if (
  51. (segments[1] as string) === "dimension" ||
  52. pathname?.startsWith("/dimension")
  53. )
  54. return 2;
  55. // Check Mine
  56. if (segments[1] === "mine" || pathname?.startsWith("/mine")) return 3;
  57. // Check Home (Explicit)
  58. if (
  59. (segments[1] as string) === "index" ||
  60. pathname === "/" ||
  61. pathname === "/index"
  62. )
  63. return 0;
  64. return -1;
  65. };
  66. const [currentIndex, setCurrentIndex] = React.useState(() => {
  67. const idx = getTabIndex();
  68. return idx === -1 ? 0 : idx;
  69. });
  70. React.useEffect(() => {
  71. const idx = getTabIndex();
  72. if (idx !== -1) {
  73. setCurrentIndex(idx);
  74. }
  75. }, [segments, pathname]);
  76. const handlePress = (index: number) => {
  77. const route = tabList[index].route;
  78. router.replace(route as any);
  79. };
  80. return (
  81. <View style={styles.wrapper}>
  82. {/* Replaced ImageBackground with View for Cyberpunk style */}
  83. <View style={styles.container}>
  84. <View style={styles.center}>
  85. {tabList.map((item, index) => {
  86. const isActive = currentIndex === index;
  87. return (
  88. <TouchableOpacity
  89. key={index}
  90. style={styles.item}
  91. activeOpacity={0.8}
  92. onPress={() => handlePress(index)}
  93. >
  94. <View
  95. style={[
  96. styles.iconContainer,
  97. isActive && styles.activeIconContainer,
  98. ]}
  99. >
  100. <Image
  101. source={isActive ? item.active : item.img}
  102. style={styles.icon}
  103. contentFit="contain"
  104. />
  105. </View>
  106. <Text
  107. style={[
  108. styles.label,
  109. { color: isActive ? Colors.neonBlue : Colors.textTertiary },
  110. ]}
  111. >
  112. {item.name}
  113. </Text>
  114. </TouchableOpacity>
  115. );
  116. })}
  117. </View>
  118. </View>
  119. </View>
  120. );
  121. }
  122. const styles = StyleSheet.create({
  123. wrapper: {
  124. position: "absolute",
  125. bottom: 0,
  126. left: 0,
  127. right: 0,
  128. zIndex: 999,
  129. },
  130. container: {
  131. width: SCREEN_WIDTH,
  132. height: 85, // Slightly taller for labels
  133. backgroundColor: Colors.darkBg,
  134. borderTopWidth: 1,
  135. borderTopColor: Colors.neonBlue,
  136. borderTopLeftRadius: 20,
  137. borderTopRightRadius: 20,
  138. // Neon Glow
  139. shadowColor: Colors.neonBlue,
  140. shadowOffset: { width: 0, height: -2 },
  141. shadowOpacity: 0.3,
  142. shadowRadius: 10,
  143. elevation: 10,
  144. paddingBottom: 20, // For home indicator
  145. },
  146. center: {
  147. flex: 1,
  148. flexDirection: "row",
  149. alignItems: "center",
  150. },
  151. item: {
  152. flex: 1,
  153. justifyContent: "center",
  154. alignItems: "center",
  155. paddingTop: 10,
  156. },
  157. iconContainer: {
  158. width: 28,
  159. height: 28,
  160. marginBottom: 4,
  161. justifyContent: "center",
  162. alignItems: "center",
  163. },
  164. activeIconContainer: {
  165. // Optional: Add glow behind active icon
  166. },
  167. icon: {
  168. width: "100%",
  169. height: "100%",
  170. },
  171. label: {
  172. fontSize: 10,
  173. fontWeight: "bold",
  174. },
  175. });