import { Image } from 'expo-image'; import React, { useEffect, useRef, useState } from 'react'; import { Animated, Dimensions, ScrollView, StyleSheet, View } from 'react-native'; import { Images } from '@/constants/images'; import TransCard from './TransCard'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); // 3 columns. Uniapp: 220rpx (approx 30%). // Let's use 3 columns with some spacing. const COLUMNS = 3; const ITEM_WIDTH = (SCREEN_WIDTH - 40) / COLUMNS; // 20px padding left/right const ITEM_HEIGHT = ITEM_WIDTH * 1.5; // Aspect ratio interface FlipCardProps { item: any; index: number; delay: number; onFlipComplete?: () => void; } const FlipCard = ({ item, index, delay, onFlipComplete }: FlipCardProps) => { const animValue = useRef(new Animated.Value(0)).current; useEffect(() => { Animated.sequence([ Animated.delay(delay), Animated.timing(animValue, { toValue: 180, // We'll map 0-180 to the flip duration: 600, // Flip duration useNativeDriver: true, }) ]).start(() => { onFlipComplete && onFlipComplete(); }); }, [delay]); // Front (Back Image) Interpolation: 0 -> 90 derived from 0->180 range // Actually standard flip: // Front: 0deg to 180deg (starts visible, goes back) // Back: 180deg to 360deg (starts invisible back, goes front) // Uniapp Logic: // Back (Card Back): rotateY 0deg -> 90deg (Hide) // Front (Result): rotateY -90deg -> 0deg (Show) // Let's use animValue 0 -> 1. // 0 -> 0.5: Back rotates 0 -> 90. // 0.5 -> 1: Front rotates -90 -> 0. const backStyle = { transform: [ { rotateY: animValue.interpolate({ inputRange: [0, 90], outputRange: ['0deg', '90deg'], extrapolate: 'clamp', }) } ], opacity: animValue.interpolate({ inputRange: [0, 89, 90], outputRange: [1, 1, 0], // Hide when 90 to prevent z-fighting/artifacts extrapolate: 'clamp', }) }; const frontStyle = { transform: [ { rotateY: animValue.interpolate({ inputRange: [90, 180], outputRange: ['-90deg', '0deg'], // or 270 to 360 extrapolate: 'clamp', }) } ], opacity: animValue.interpolate({ inputRange: [0, 90, 91], outputRange: [0, 0, 1], extrapolate: 'clamp', }) }; return ( {/* Card Back (Initially Visible) */} {/* Result Face (Initially Hidden) */} ); }; interface LotteryGridProps { results: any[]; onFinish: () => void; } export default function LotteryGrid({ results, onFinish }: LotteryGridProps) { // Determine staggered delay // 10 items. // We want them to flip sequentially or semi-sequentially. // Uniapp likely does it index based. const [completedCount, setCompletedCount] = useState(0); const handleFlipComplete = () => { setCompletedCount(prev => { const newCount = prev + 1; if (newCount === results.length) { // All done setTimeout(onFinish, 1000); // Wait a bit before finish } return newCount; }); }; return ( {results.map((item, index) => ( ))} ); } const styles = StyleSheet.create({ container: { flex: 1, width: '100%', }, scrollContent: { paddingTop: 20, // Reduced from 100 to avoid being too low (parent has margin) paddingBottom: 150, // Increased to avoid overlapping with absolute bottom button }, grid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', }, cardContainer: { width: ITEM_WIDTH, height: ITEM_HEIGHT, alignItems: 'center', justifyContent: 'center', marginVertical: 10, }, cardFace: { position: 'absolute', width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center', backfaceVisibility: 'hidden', // Android support varies, relying on opacity/rotation logic } });