| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- import React, { useEffect, useRef } from 'react';
- import { Animated, Easing, StyleSheet, View } from 'react-native';
- import TransCard from './TransCard';
- interface LotteryReelProps {
- pool: any[];
- result: any;
- width: number;
- itemWidth: number;
- duration?: number;
- index?: number;
- delay?: number;
- }
- export default function LotteryReel({
- pool,
- result,
- width,
- itemWidth,
- duration = 3000,
- index = 0,
- delay = 0,
- }: LotteryReelProps) {
- const slideAnim = useRef(new Animated.Value(0)).current;
- // ... (buildReelData remains same) ...
- const buildReelData = () => {
- // Guard
- if (!pool || pool.length === 0) {
- // Log fallback
- console.log(`Reel ${index}: Empty pool, using fallback`);
- const fallback = result ? [result, result, result, result, result] : [];
- return {
- reelData: fallback.map((item, idx) => ({ ...item, uniqueKey: `fb-${idx}` })),
- targetOffset: 0
- };
- }
- let basePool = [...pool];
-
- // Ensure base pool is large enough
- if (basePool.length > 0) {
- while (basePool.length < 10) {
- basePool = [...basePool, ...basePool];
- }
- }
-
- // Shuffle and expand
- // Use consistent seed or just math random
- const shuffle = (arr: any[]) => [...arr].sort(() => Math.random() - 0.5);
- let array = [...shuffle(basePool)];
-
- if (basePool.length > 0) {
- while (array.length < 25) { // Reduced from 50 to 25 to minimize cumulative render error
- array = [...array, ...shuffle(basePool)];
- }
- }
-
- // Set target
- const activeIndex = array.length - 5; // Target is closer to end
-
- // Inject result with winner flag
- array[activeIndex] = { ...result, key: `res-${index}`, isWinner: true };
-
- const reelData = array.map((item, idx) => ({ ...item, uniqueKey: `${idx}-${item.id || item.goodsId || idx}` }));
-
- // Calculate target offset
- // TranslateX moves LEFT, so negative.
- // We want the center of item at activeIndex to be at center of View (width/2)
- // Item center = activeIndex * itemWidth + itemWidth/2
- // Offset = width/2 - Item center
- const targetOffset = width / 2 - (activeIndex * itemWidth + itemWidth / 2);
-
- console.log(`Reel ${index} (Final): poolLength=${pool.length}, reelLength=${reelData.length}, activeIndex=${activeIndex}, target=${targetOffset}`);
- return { reelData, targetOffset };
- };
- const { reelData, targetOffset } = React.useMemo(() => buildReelData(), [pool, result, width]);
- useEffect(() => {
- if (targetOffset !== 0) {
- // Reset to 0 first
- slideAnim.setValue(0);
-
- // Use requestAnimationFrame to ensure render is complete before animating
- // helping with the "stuck" feel during initial heavy mount
- requestAnimationFrame(() => {
- Animated.sequence([
- Animated.delay(delay),
- Animated.timing(slideAnim, {
- toValue: targetOffset,
- duration: duration,
- easing: Easing.out(Easing.cubic),
- useNativeDriver: true,
- })
- ]).start();
- });
- }
- }, [targetOffset, delay]);
- return (
- <View style={[styles.container, { width }]}>
- <Animated.View style={[styles.reel, { transform: [{ translateX: slideAnim }] }]}>
- {reelData.map((item, idx) => (
- <View key={item.uniqueKey} style={[styles.itemWrapper, { width: itemWidth }]}>
- <TransCard
- item={item}
- width={itemWidth === 94 ? 90 : itemWidth - 4}
- height={itemWidth === 94 ? 121 : 90}
- fill={itemWidth === 94 ? 2 : 2}
- imageWidth={itemWidth === 94 ? 74 : 55}
- imageHeight={itemWidth === 94 ? 94 : 70}
- />
- </View>
- ))}
- </Animated.View>
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: {
- overflow: 'hidden',
- },
- reel: {
- flexDirection: 'row',
- },
- itemWrapper: {
- justifyContent: 'center',
- alignItems: 'center',
- },
- });
|