You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

156 lines
3.9 KiB

/**
* 轮播图组件
*
* 展示首页轮播图,支持自动播放和手动滑动
* 使用真实数据
*/
import React, { useState, useEffect, useRef, useCallback } from 'react';
import {
View,
Image,
TouchableOpacity,
ScrollView,
NativeScrollEvent,
NativeSyntheticEvent,
ActivityIndicator,
Dimensions,
Alert,
} from 'react-native';
import Colors from '@/constants/Colors';
// import type { Banner } from '@/types/home';
import { styles } from './styles';
import useMsgStore from '@/stores/msgStore';
interface BannerSwiperProps {
theme: 'light' | 'dark';
}
const { width } = Dimensions.get('window');
/**
* 轮播图组件
*/
export default function BannerSwiper({ theme }: BannerSwiperProps) {
const s = styles[theme];
const [currentIndex, setCurrentIndex] = useState(0);
const [loading, setLoading] = useState(true);
const scrollViewRef = useRef<ScrollView>(null);
const autoPlayTimerRef = useRef<any>(null);
const { homeBanner } = useMsgStore();
// 加载轮播图数据
useEffect(() => {
// 如果有传入的 banners 数据,直接使用
if (homeBanner.length > 0) {
setLoading(false);
return;
}
// 如果没有数据,保持 loading 状态显示骨架屏
}, [homeBanner]);
// 处理 Banner 点击
const onBannerPress = useCallback((banner: Record<string, any>) => {
Alert.alert('轮播图', `点击了: ${banner.title || banner.id}`);
// 这里可以添加导航逻辑
}, []);
// 处理滚动事件
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
const contentOffsetX = event.nativeEvent.contentOffset.x;
const index = Math.round(contentOffsetX / (width - 24));
setCurrentIndex(Math.min(index, homeBanner.length - 1));
};
// 启动自动播放
const startAutoPlay = useCallback(() => {
if (homeBanner.length <= 1) return;
autoPlayTimerRef.current = setInterval(() => {
setCurrentIndex((prev) => {
const nextIndex = (prev + 1) % homeBanner.length;
scrollViewRef.current?.scrollTo({
x: nextIndex * (width - 24),
animated: true,
});
return nextIndex;
});
}, 5000);
}, [homeBanner.length]);
// 停止自动播放
const stopAutoPlay = useCallback(() => {
if (autoPlayTimerRef.current) {
clearInterval(autoPlayTimerRef.current);
}
}, []);
// 自动播放
useEffect(() => {
if (!loading && homeBanner.length > 0) {
startAutoPlay();
}
return () => stopAutoPlay();
}, [loading, homeBanner.length, startAutoPlay, stopAutoPlay]);
// 骨架屏 - 加载中显示占位符
if (loading || homeBanner.length === 0) {
return (
<View style={s.container}>
<View
style={[
s.image,
{
backgroundColor: theme === 'dark' ? '#333' : '#e0e0e0',
},
]}
/>
</View>
);
}
return (
<View style={s.container}>
<ScrollView
ref={scrollViewRef}
style={s.scrollView}
horizontal
pagingEnabled
scrollEventThrottle={16}
onScroll={handleScroll}
showsHorizontalScrollIndicator={false}
onMomentumScrollBegin={stopAutoPlay}
onMomentumScrollEnd={startAutoPlay}
>
{homeBanner.map((banner) => (
<TouchableOpacity
key={banner.id}
onPress={() => onBannerPress(banner)}
activeOpacity={0.9}
>
<Image
source={{ uri: banner.subject }}
style={s.image}
resizeMode="cover"
/>
</TouchableOpacity>
))}
</ScrollView>
{/* 指示器 */}
<View style={s.indicatorContainer}>
{homeBanner.map((_, index) => (
<View
key={index}
style={[
s.indicator,
index === currentIndex && s.indicatorActive,
]}
/>
))}
</View>
</View>
);
}