/** * 游戏分类菜单组件 * * 展示游戏分类,支持切换,使用真实数据 */ import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react'; import { View, Text, ScrollView, TouchableOpacity, Animated, Image, Platform } from 'react-native'; // import type { GameCategory } from '@/types/home'; import { styles } from './styles'; import { useGameMainMenus, useMenuDataLoaded, useSelectedCategory } from '@/hooks/useGameMenus'; // import useGameStore from '@/stores/gameStore'; // import { ThemeEnum } from '@/constants/theme'; import { Colors, useColorScheme } from '@/theme'; // 条件导入 LinearGradient - 仅在非 Web 平台使用 let LinearGradient: any = null; if (Platform.OS !== 'web') { LinearGradient = require('react-native-linear-gradient').default; } // 游戏菜单图片映射 - 使用 require 加载本地资源 const MENU_ICON_MAP: Record = { 'recommend': require('../../../../assets/images/game/menu/recommend.png'), 'chess': require('../../../../assets/images/game/menu/chess.png'), 'electronic': require('../../../../assets/images/game/menu/electronic.png'), 'fishing': require('../../../../assets/images/game/menu/fishing.png'), 'lottery': require('../../../../assets/images/game/menu/lottery.png'), 'sports': require('../../../../assets/images/game/menu/sports.png'), 'trial': require('../../../../assets/images/game/menu/trial.png'), 'blockThird': require('../../../../assets/images/game/menu/blockThird.png'), 'clock-solid': require('../../../../assets/images/game/menu/clock-solid.png'), 'clock-solid_dark': require('../../../../assets/images/game/menu/clock-solid_dark.png'), 'home-star-solid': require('../../../../assets/images/game/menu/home-star-solid.png'), 'live': require('../../../../assets/images/game/menu/live.png'), }; interface GameMainMenuProps { topHeight?: number; showSubMenus?: boolean; } /** * 游戏分类菜单组件 */ export default function GameMainMenu({ topHeight = 0, showSubMenus = true, }: GameMainMenuProps) { const theme = useColorScheme(); const s = styles[theme]; const scrollViewRef = useRef(null); const gameMenus = useGameMainMenus(theme); // 从 hook 获取选中分类和更新方法 const { selectedCategory, setSelectedCategory } = useSelectedCategory(); // 检查数据加载完成 const isDataLoaded = useMenuDataLoaded(); // 使用 useMemo 缓存找到的索引,避免每次都重新计算 const selectedIndex = useMemo(() => { return gameMenus.findIndex((cat) => cat.key === selectedCategory); }, [selectedCategory, gameMenus]); // 当分类改变时,滚动到该分类 useEffect(() => { if (selectedIndex >= 0) { scrollViewRef.current?.scrollTo({ x: selectedIndex * 100, animated: true, }); } }, [selectedIndex]); // 使用 useCallback 稳定 onPress 回调 const handleCategoryPress = useCallback( (categoryKey: string) => { setSelectedCategory(categoryKey); }, [setSelectedCategory] ); // 骨架屏 - 显示加载中的占位符 const renderSkeleton = () => ( {Array.from({ length: 6 }).map((_, index) => ( ))} ); console.log('isDataLoaded', isDataLoaded); // 如果动态数据还未加载,显示骨架屏 if (!isDataLoaded) { return renderSkeleton(); } return ( {gameMenus.map((menu) => { // 处理图片源 - 优先使用 logo(URL),其次使用 icon(本地资源) let imageSource: any = null; if (menu.logo) { // logo 是 URL,直接使用 imageSource = { uri: menu.logo }; } else if (menu.icon) { // icon 是本地资源名称,从映射表中获取 imageSource = MENU_ICON_MAP[menu.icon]; } const isActive = selectedCategory === menu.key; const themeColors = Colors[theme]; // 获取渐变色 - 从主题色到透明 const gradientStart = `${themeColors.tint}40`; // 主题色 + 40% 透明度 const gradientEnd = `${themeColors.tint}00`; // 完全透明 const menuContent = ( <> {imageSource && ( )} {menu.name} ); // 如果是选中状态,使用 LinearGradient 包装(非 Web 平台)或 CSS 渐变(Web 平台) if (isActive) { // Web 平台使用 CSS 渐变 if (Platform.OS === 'web') { const webStyle = { ...s.menuItem, ...s.menuItemActive, background: `linear-gradient(to top, ${gradientStart}, ${gradientEnd})`, backgroundColor: undefined, // 移除 backgroundColor,使用 background } as any; return ( handleCategoryPress(menu.key)} activeOpacity={0.7} > {menuContent} ); } // 非 Web 平台使用 LinearGradient if (LinearGradient) { return ( handleCategoryPress(menu.key)} activeOpacity={0.7} > {menuContent} ); } // 备用方案:如果 LinearGradient 不可用,使用纯色 return ( handleCategoryPress(menu.key)} activeOpacity={0.7} > {menuContent} ); } // 未选中状态,使用普通 TouchableOpacity return ( handleCategoryPress(menu.key)} activeOpacity={0.7} > {menuContent} ); })} ); }