Files

215 lines
7.3 KiB
TypeScript
Raw Permalink Normal View History

2025-11-11 18:48:54 +08:00
/**
*
*
* 使
*/
import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react';
2025-11-12 00:13:26 +08:00
import { View, Text, ScrollView, TouchableOpacity, Animated, Image, Platform } from 'react-native';
2025-11-11 18:48:54 +08:00
// import type { GameCategory } from '@/types/home';
2025-11-12 00:13:26 +08:00
import { styles } from './styles';
2025-11-13 16:47:10 +08:00
import { useGameMainMenus, useMenuDataLoaded, useGameMenuTabs } from '@/hooks/useGameMenus';
2025-11-11 18:48:54 +08:00
// import useGameStore from '@/stores/gameStore';
2025-11-12 00:13:26 +08:00
// import { ThemeEnum } from '@/constants/theme';
import { Colors, useColorScheme } from '@/theme';
2025-11-11 18:48:54 +08:00
2025-11-12 00:13:26 +08:00
// 条件导入 LinearGradient - 仅在非 Web 平台使用
let LinearGradient: any = null;
if (Platform.OS !== 'web') {
LinearGradient = require('react-native-linear-gradient').default;
}
2025-11-11 18:48:54 +08:00
2025-11-12 00:13:26 +08:00
// 游戏菜单图片映射 - 使用 require 加载本地资源
const MENU_ICON_MAP: Record<string, any> = {
2025-11-13 16:47:10 +08:00
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'),
2025-11-12 00:13:26 +08:00
'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'),
2025-11-13 16:47:10 +08:00
live: require('../../../../assets/images/game/menu/live.png'),
2025-11-12 00:13:26 +08:00
};
2025-11-11 18:48:54 +08:00
interface GameMainMenuProps {
topHeight?: number;
showSubMenus?: boolean;
}
/**
*
*/
2025-11-13 16:47:10 +08:00
export default function GameMainMenu({ topHeight = 0, showSubMenus = true }: GameMainMenuProps) {
2025-11-12 00:13:26 +08:00
const theme = useColorScheme();
2025-11-11 18:48:54 +08:00
const s = styles[theme];
const scrollViewRef = useRef<ScrollView>(null);
const gameMenus = useGameMainMenus(theme);
2025-11-12 00:13:26 +08:00
// 从 hook 获取选中分类和更新方法
2025-11-13 16:47:10 +08:00
const { activeMainMenuTab, setActiveMainMenuTab } = useGameMenuTabs();
2025-11-12 00:13:26 +08:00
2025-11-11 18:48:54 +08:00
// 检查数据加载完成
const isDataLoaded = useMenuDataLoaded();
// 使用 useMemo 缓存找到的索引,避免每次都重新计算
const selectedIndex = useMemo(() => {
2025-11-13 16:47:10 +08:00
return gameMenus.findIndex((cat) => cat.key === activeMainMenuTab);
}, [activeMainMenuTab, gameMenus]);
2025-11-11 18:48:54 +08:00
// 当分类改变时,滚动到该分类
useEffect(() => {
if (selectedIndex >= 0) {
scrollViewRef.current?.scrollTo({
x: selectedIndex * 100,
animated: true,
});
}
}, [selectedIndex]);
// 使用 useCallback 稳定 onPress 回调
2025-11-12 00:13:26 +08:00
const handleCategoryPress = useCallback(
(categoryKey: string) => {
2025-11-13 16:47:10 +08:00
setActiveMainMenuTab(categoryKey);
2025-11-12 00:13:26 +08:00
},
2025-11-13 16:47:10 +08:00
[setActiveMainMenuTab]
2025-11-12 00:13:26 +08:00
);
2025-11-11 18:48:54 +08:00
// 骨架屏 - 显示加载中的占位符
const renderSkeleton = () => (
<View style={s.container}>
<ScrollView
style={s.scrollView}
horizontal
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
2025-11-12 00:13:26 +08:00
{Array.from({ length: 6 }).map((_, index) => (
2025-11-11 18:48:54 +08:00
<View
key={`skeleton-${index}`}
2025-11-12 00:13:26 +08:00
style={[s.menuItem, { backgroundColor: theme === 'dark' ? '#333' : '#e0e0e0' }]}
2025-11-11 18:48:54 +08:00
/>
))}
</ScrollView>
</View>
);
2025-11-12 00:13:26 +08:00
console.log('isDataLoaded', isDataLoaded);
2025-11-11 18:48:54 +08:00
// 如果动态数据还未加载,显示骨架屏
if (!isDataLoaded) {
return renderSkeleton();
}
return (
<View style={s.container}>
<ScrollView
ref={scrollViewRef}
style={s.scrollView}
horizontal
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
2025-11-12 00:13:26 +08:00
{gameMenus.map((menu) => {
// 处理图片源 - 优先使用 logoURL其次使用 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];
}
2025-11-13 16:47:10 +08:00
const isActive = activeMainMenuTab === menu.key;
2025-11-12 00:13:26 +08:00
const themeColors = Colors[theme];
// 获取渐变色 - 从主题色到透明
const gradientStart = `${themeColors.tint}40`; // 主题色 + 40% 透明度
2025-11-13 16:47:10 +08:00
const gradientEnd = `${themeColors.tint}00`; // 完全透明
2025-11-12 00:13:26 +08:00
const menuContent = (
<>
{imageSource && (
<Image source={imageSource} style={s.menuIcon} resizeMode="contain" />
)}
2025-11-13 16:47:10 +08:00
<Text style={[s.menuText, isActive && s.menuTextActive]}>{menu.name}</Text>
2025-11-12 00:13:26 +08:00
</>
);
// 如果是选中状态,使用 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 (
<TouchableOpacity
key={menu.key}
style={webStyle}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menuContent}
</TouchableOpacity>
);
}
// 非 Web 平台使用 LinearGradient
if (LinearGradient) {
return (
<LinearGradient
key={menu.key}
colors={[gradientStart, gradientEnd]}
start={{ x: 0.5, y: 1 }} // 从下往上
end={{ x: 0.5, y: 0 }}
style={[s.menuItem, s.menuItemActive]}
>
<TouchableOpacity
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menuContent}
</TouchableOpacity>
</LinearGradient>
);
}
// 备用方案:如果 LinearGradient 不可用,使用纯色
return (
<TouchableOpacity
key={menu.key}
style={[s.menuItem, s.menuItemActive]}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menuContent}
</TouchableOpacity>
);
}
// 未选中状态,使用普通 TouchableOpacity
return (
<TouchableOpacity
key={menu.key}
style={s.menuItem}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
2025-11-11 18:48:54 +08:00
>
2025-11-12 00:13:26 +08:00
{menuContent}
</TouchableOpacity>
);
})}
2025-11-11 18:48:54 +08:00
</ScrollView>
</View>
);
}