| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- import { BannerItem } from '@/services/base';
- import { Image } from 'expo-image';
- import React, { useEffect, useRef, useState } from 'react';
- import { Dimensions, FlatList, StyleSheet, TouchableOpacity, View } from 'react-native';
- const { width: SCREEN_WIDTH } = Dimensions.get('window');
- // 小程序 banner 高度 432rpx,宽度100%
- const BANNER_HEIGHT = 216; // 432rpx / 2
- interface BannerProps {
- data: BannerItem[];
- onPress?: (item: BannerItem) => void;
- }
- export function Banner({ data, onPress }: BannerProps) {
- const [activeIndex, setActiveIndex] = useState(0);
- const flatListRef = useRef<FlatList<BannerItem>>(null);
- const autoPlayRef = useRef<ReturnType<typeof setInterval> | null>(null);
- useEffect(() => {
- if (data.length <= 1) return;
- autoPlayRef.current = setInterval(() => {
- const nextIndex = (activeIndex + 1) % data.length;
- flatListRef.current?.scrollToIndex({ index: nextIndex, animated: true });
- setActiveIndex(nextIndex);
- }, 5000);
- return () => {
- if (autoPlayRef.current) clearInterval(autoPlayRef.current);
- };
- }, [activeIndex, data.length]);
- const renderItem = ({ item }: { item: BannerItem }) => (
- <TouchableOpacity activeOpacity={0.9} onPress={() => onPress?.(item)}>
- <Image
- source={item.cover}
- style={styles.bannerImage}
- contentFit="cover"
- />
- </TouchableOpacity>
- );
- const onScroll = (event: any) => {
- const offsetX = event.nativeEvent.contentOffset.x;
- const index = Math.round(offsetX / SCREEN_WIDTH);
- if (index !== activeIndex && index >= 0 && index < data.length) {
- setActiveIndex(index);
- }
- };
- return (
- <View style={styles.container}>
- <FlatList
- ref={flatListRef}
- data={data}
- renderItem={renderItem}
- keyExtractor={(item, index) => item.id || String(index)}
- horizontal
- pagingEnabled
- showsHorizontalScrollIndicator={false}
- onScroll={onScroll}
- scrollEventThrottle={16}
- getItemLayout={(_, index) => ({
- length: SCREEN_WIDTH,
- offset: SCREEN_WIDTH * index,
- index,
- })}
- />
- <View style={styles.dots}>
- {data.map((_, index) => (
- <View key={index} style={[styles.dot, activeIndex === index && styles.dotActive]} />
- ))}
- </View>
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: {
- width: '100%',
- height: BANNER_HEIGHT,
- marginTop: 11,
- overflow: 'hidden',
- },
- bannerImage: {
- width: SCREEN_WIDTH,
- height: BANNER_HEIGHT,
- },
- dots: {
- position: 'absolute',
- bottom: 10,
- left: 0,
- right: 0,
- flexDirection: 'row',
- justifyContent: 'center',
- },
- dot: {
- width: 6,
- height: 6,
- borderRadius: 3,
- backgroundColor: 'rgba(255,255,255,0.5)',
- marginHorizontal: 3,
- },
- dotActive: {
- backgroundColor: '#fff',
- },
- });
|