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 ( {reelData.map((item, idx) => ( ))} ); } const styles = StyleSheet.create({ container: { overflow: 'hidden', }, reel: { flexDirection: 'row', }, itemWrapper: { justifyContent: 'center', alignItems: 'center', }, });