import { Ionicons } from '@expo/vector-icons'; import { BlurView } from 'expo-blur'; import { Tabs } from 'expo-router/js-tabs'; import type { BottomTabBarProps } from 'expo-router/js-tabs'; import { useEffect, useRef } from 'react'; import type { ComponentProps, ReactNode } from 'react'; import { Animated, Pressable, StyleSheet, Text, View, type ColorValue } from 'react-native'; import { colors } from '../../src/theme'; type IconName = ComponentProps['name']; type TabIconProps = { focused: boolean; color: ColorValue; size: number }; /** 内容底部预留高度:悬浮 Tab 栏高度 + 悬浮间距 + 余量 */ const TAB_BAR_FLOAT_SPACE = 84; /** * 选中/未选中切换实心与描边图标 */ function tabIcon(filled: IconName, outline: IconName) { return ({ color, size, focused }: TabIconProps) => ( ); } /** 单个 Tab 项:选中时图标轻弹 + 浅蓝胶囊高亮 */ function TabItem({ focused, label, icon, onPress, onLongPress, }: { focused: boolean; label: string; icon?: (props: TabIconProps) => ReactNode; onPress: () => void; onLongPress: () => void; }) { // 图标弹跳动画:选中 1.15,未选中回到 1 const scale = useRef(new Animated.Value(1)).current; useEffect(() => { Animated.spring(scale, { toValue: focused ? 1.15 : 1, useNativeDriver: true, friction: 6, tension: 200, }).start(); }, [focused, scale]); const color = focused ? colors.primary : colors.textSecondary; return ( {icon?.({ focused, color, size: 22 })} {label} ); } /** 悬浮毛玻璃 Tab 栏 */ function FloatingTabBar({ state, descriptors, navigation, insets }: BottomTabBarProps) { return ( {state.routes.map((route, index) => { const { options } = descriptors[route.key]; const focused = state.index === index; const label = typeof options.tabBarLabel === 'string' ? options.tabBarLabel : options.title ?? ''; const onPress = () => { const event = navigation.emit({ type: 'tabPress', target: route.key, canPreventDefault: true, }); if (!focused && !event.defaultPrevented) { navigation.navigate(route.name); } }; const onLongPress = () => navigation.emit({ type: 'tabLongPress', target: route.key }); return ( ); })} ); } /** * 底部 Tab 导航:悬浮毛玻璃胶囊 + 选中高亮 + 图标弹跳 */ export default function TabLayout() { return ( } > ); } const styles = StyleSheet.create({ tabBarWrap: { position: 'absolute', left: 16, right: 16, bottom: 0, }, // 外层负责 iOS 阴影(不裁剪,阴影在圆角外正常显示) tabBarShadow: { borderRadius: 24, shadowColor: colors.textPrimary, shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.15, shadowRadius: 16, }, // 内层 BlurView:真毛玻璃(iOS)/ 半透明(Android 31 以下降级)+ Android 阴影 tabBar: { flexDirection: 'row', alignItems: 'center', borderRadius: 24, paddingHorizontal: 6, paddingVertical: 8, overflow: 'hidden', elevation: 10, }, tabItem: { flex: 1, alignItems: 'center', justifyContent: 'center', }, tabItemContent: { alignItems: 'center', justifyContent: 'center', paddingHorizontal: 12, paddingVertical: 6, borderRadius: 18, }, tabItemContentActive: { backgroundColor: colors.primaryLight, }, tabLabel: { marginTop: 4, fontSize: 11, fontWeight: '600', }, });