2025-07-29 20:38:00 +08:00

171 lines
7.4 KiB
TypeScript

import ChatInSvg from "@/assets/icons/svg/chatIn.svg";
import ChatNotInSvg from "@/assets/icons/svg/chatNotIn.svg";
import PersonInSvg from "@/assets/icons/svg/personIn.svg";
import PersonNotInSvg from "@/assets/icons/svg/personNotIn.svg";
import { router, usePathname } from "expo-router";
import React, { useCallback, useEffect, useMemo } from 'react';
import { Dimensions, Image, StyleSheet, TouchableOpacity, View } from 'react-native';
import Svg, { Circle, Ellipse, G, Mask, Path, Rect } from "react-native-svg";
// 使用 React.memo 包装 SVG 组件,避免不必要的重渲染
const TabIcon = React.memo(({ isActive, ActiveIcon, InactiveIcon }: {
isActive: boolean;
ActiveIcon: React.FC<{ width: number; height: number }>;
InactiveIcon: React.FC<{ width: number; height: number }>;
}) => {
const Icon = isActive ? ActiveIcon : InactiveIcon;
return <Icon width={24} height={24} />;
});
// 提取 SVG 组件,避免重复渲染
const CenterButtonSvg = React.memo(() => (
<Svg width="100%" height="100%" viewBox="0 0 85 85" fill="none">
<Mask id="mask0_1464_1669" maskUnits="userSpaceOnUse" x="0" y="0" width="85" height="85">
<Circle cx="42.5" cy="42.5" r="42.5" fill="#FFC959" />
</Mask>
<G mask="url(#mask0_1464_1669)">
<Circle cx="42.5" cy="42.5" r="42.5" fill="#FFD38D" />
<Path d="M20.2018 14.6411C21.8694 12.5551 26.4765 16.939 28.5716 19.3917L20.8604 20.0277C19.3178 19.3509 18.5342 16.7271 20.2018 14.6411Z" fill="#FFDBA3" />
<Path d="M21.3021 15.4913C22.503 13.6451 25.3001 17.1089 26.5485 19.0716L22.7323 19.2755C21.7552 18.7834 20.1012 17.3376 21.3021 15.4913Z" fill="#AC7E35" />
<Path d="M65.1253 14.6411C63.4577 12.5551 58.8506 16.939 56.7556 19.3917L64.4667 20.0277C66.0093 19.3509 66.7929 16.7271 65.1253 14.6411Z" fill="#FFDBA3" />
<Path d="M64.0255 15.4913C62.8246 13.6451 60.0276 17.1089 58.7792 19.0716L62.5953 19.2755C63.5724 18.7834 65.2264 17.3376 64.0255 15.4913Z" fill="#AC7E35" />
<Path d="M-15.3352 49.1734C10.3693 4.65192 74.6306 4.65187 100.335 49.1734L117.868 79.5409C143.572 124.062 111.442 179.714 60.0327 179.714H24.9673C-26.4417 179.714 -58.5724 124.062 -32.8679 79.5409L-15.3352 49.1734Z" fill="#FFD18A" />
<Rect x="38.5571" y="46.2812" width="2.62922" height="3.68091" rx="1.31461" transform="rotate(-180 38.5571 46.2812)" fill="#4C320C" />
<Rect x="48.0205" y="46.2812" width="2.62922" height="3.68091" rx="1.31461" transform="rotate(-180 48.0205 46.2812)" fill="#4C320C" />
<Path d="M4.8084 73.2062C22.9876 46.7781 62.0132 46.7782 80.1924 73.2062L100.897 103.306C121.776 133.659 100.046 174.982 63.2051 174.982H21.7957C-15.0453 174.982 -36.7756 133.659 -15.8963 103.306L4.8084 73.2062Z" fill="#FFF8DE" />
<Ellipse cx="79.047" cy="68.6298" rx="43.1193" ry="30.7619" fill="#FFF8DE" />
<Ellipse cx="5.69032" cy="68.6298" rx="42.8563" ry="30.7619" fill="#FFF8DE" />
<Ellipse cx="42.2365" cy="53.3803" rx="3.15507" ry="2.3663" transform="rotate(180 42.2365 53.3803)" fill="#FFB8B9" />
<Path d="M41.7813 56.0095C41.9837 55.6589 42.4897 55.6589 42.6921 56.0095L43.1475 56.7982C43.3499 57.1488 43.0969 57.587 42.6921 57.587H41.7813C41.3765 57.587 41.1235 57.1488 41.3259 56.7982L41.7813 56.0095Z" fill="#4C320C" />
</G>
</Svg>
));
const AskNavbar = () => {
// 获取设备尺寸
const { width } = useMemo(() => Dimensions.get('window'), []);
const pathname = usePathname();
// 预加载目标页面
useEffect(() => {
const preloadPages = async () => {
try {
await Promise.all([
router.prefetch('/memo-list'),
router.prefetch('/ask'),
router.prefetch('/owner')
]);
} catch (error) {
console.warn('预加载页面失败:', error);
}
};
preloadPages();
}, []);
// 使用 useCallback 缓存导航函数
const navigateTo = useCallback((route: string) => {
if (route === '/ask') {
router.push({
pathname: '/ask',
params: { newSession: "true" }
});
} else {
router.push(route as any);
}
}, []);
// 使用 useMemo 缓存样式对象
const styles = useMemo(() => StyleSheet.create({
container: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'white',
shadowColor: '#000',
shadowOffset: { width: 0, height: -4 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 10,
},
backgroundImage: {
width,
height: 80,
resizeMode: 'cover'
},
navButton: {
width: width / 2, // 半屏宽度
height: 80, // 与 navbar 高度相同
justifyContent: 'center',
alignItems: 'center'
},
navContainer: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 80, // Set a fixed height for the navbar
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 32,
backgroundColor: 'transparent', // Make sure it's transparent
},
centerButton: {
position: 'absolute',
left: width / 2,
top: -42.5, // Adjust this value to move the button up or down
marginLeft: -42.5, // Half of the button width (85/2)
width: 85,
height: 85,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#FFB645',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
borderRadius: 50,
backgroundColor: 'transparent',
zIndex: 10,
}
}), [width]);
return (
<View style={styles.container}>
<Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: width, height: 80, resizeMode: 'cover' }} />
<View style={styles.navContainer}>
<TouchableOpacity
onPress={() => navigateTo('/memo-list')}
style={[styles.navButton, { alignItems: "flex-start", paddingLeft: 16 }]}
>
<TabIcon
isActive={pathname === "/memo-list"}
ActiveIcon={ChatInSvg}
InactiveIcon={ChatNotInSvg}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigateTo('/ask')}
style={styles.centerButton}
>
<CenterButtonSvg />
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigateTo('/owner')}
style={styles.navButton}
>
<TabIcon
isActive={pathname === "/owner"}
ActiveIcon={PersonInSvg}
InactiveIcon={PersonNotInSvg}
/>
</TouchableOpacity>
</View>
</View>
);
};
export default React.memo(AskNavbar);