| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- 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 (
- <View style={styles.cardContainer}>
- {/* Card Back (Initially Visible) */}
- <Animated.View style={[styles.cardFace, backStyle]}>
- <Image
- source={{ uri: Images.box.back }}
- style={{ width: '100%', height: '100%' }}
- contentFit="contain"
- />
- </Animated.View>
- {/* Result Face (Initially Hidden) */}
- <Animated.View style={[styles.cardFace, frontStyle]}>
- <TransCard
- item={item}
- width={ITEM_WIDTH - 10} // Padding inside
- height={ITEM_HEIGHT - 10}
- fill={0}
- imageWidth={(ITEM_WIDTH - 20) * 0.8}
- imageHeight={(ITEM_HEIGHT - 20) * 0.7}
- />
- </Animated.View>
- </View>
- );
- };
- 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 (
- <ScrollView style={styles.container} contentContainerStyle={styles.scrollContent}>
- <View style={styles.grid}>
- {results.map((item, index) => (
- <FlipCard
- key={index}
- item={item}
- index={index}
- delay={index * 200} // 200ms stagger
- onFlipComplete={handleFlipComplete}
- />
- ))}
- </View>
- </ScrollView>
- );
- }
- 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
- }
- });
|