ProductSwiper.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import { Image } from 'expo-image';
  2. import React, { useState } from 'react';
  3. import {
  4. Dimensions,
  5. FlatList,
  6. ImageBackground,
  7. StyleSheet,
  8. Text,
  9. TouchableOpacity,
  10. View,
  11. } from 'react-native';
  12. import { Images } from '@/constants/images';
  13. const { width } = Dimensions.get('window');
  14. interface Product {
  15. cover: string;
  16. name: string;
  17. level: string;
  18. quantity?: number;
  19. }
  20. interface ProductSwiperProps {
  21. products: Product[];
  22. onShowSwipe?: (index: number) => void;
  23. }
  24. export default function ProductSwiper({ products, onShowSwipe }: ProductSwiperProps) {
  25. const [current, setCurrent] = useState(0);
  26. const getProbability = (item: Product) => {
  27. return "10%"; // Placeholder
  28. };
  29. if (!products || products.length === 0) return null;
  30. const renderItem = ({ item, index }: { item: Product; index: number }) => (
  31. <TouchableOpacity
  32. activeOpacity={0.9}
  33. onPress={() => onShowSwipe?.(index)}
  34. style={styles.itemContainer}
  35. >
  36. <View style={styles.imageBox}>
  37. <Image source={{ uri: item.cover }} style={styles.image} contentFit="contain" />
  38. </View>
  39. {/* Details Button/Tag */}
  40. <ImageBackground
  41. source={{ uri: Images.box.detail.detailsBut }}
  42. style={styles.detailsTag}
  43. resizeMode="contain"
  44. >
  45. <View style={styles.detailsContent}>
  46. <Text style={styles.levelText}>
  47. {item.level === 'A' ? '超神款' :
  48. item.level === 'B' ? '欧皇款' :
  49. item.level === 'C' ? '隐藏款' : '普通款'}
  50. </Text>
  51. <Text style={styles.probText}>{getProbability(item)}</Text>
  52. </View>
  53. </ImageBackground>
  54. {/* Name Tag */}
  55. <ImageBackground
  56. source={{ uri: Images.box.detail.nameBg }}
  57. style={styles.nameTag}
  58. resizeMode="stretch"
  59. >
  60. <Text style={styles.nameText} numberOfLines={1}>{item.name}</Text>
  61. </ImageBackground>
  62. </TouchableOpacity>
  63. );
  64. return (
  65. <View style={styles.container}>
  66. <FlatList
  67. data={products}
  68. renderItem={renderItem}
  69. horizontal
  70. pagingEnabled
  71. showsHorizontalScrollIndicator={false}
  72. keyExtractor={(_, index) => index.toString()}
  73. onMomentumScrollEnd={(e) => {
  74. const contentOffset = e.nativeEvent.contentOffset;
  75. const viewSize = e.nativeEvent.layoutMeasurement;
  76. const pageNum = Math.floor(contentOffset.x / viewSize.width);
  77. setCurrent(pageNum);
  78. }}
  79. />
  80. {/* Left/Right Arrows if needed */}
  81. {current > 0 && (
  82. <View style={[styles.arrow, styles.leftArrow]}>
  83. <Image source={{ uri: Images.box.detail.left }} style={styles.arrowImg} contentFit="contain" />
  84. </View>
  85. )}
  86. {current < products.length - 1 && (
  87. <View style={[styles.arrow, styles.rightArrow]}>
  88. <Image source={{ uri: Images.box.detail.right }} style={styles.arrowImg} contentFit="contain" />
  89. </View>
  90. )}
  91. </View>
  92. );
  93. }
  94. const styles = StyleSheet.create({
  95. container: {
  96. width: '100%',
  97. height: 380, // Adjusted height
  98. alignItems: 'center',
  99. marginTop: 20,
  100. },
  101. itemContainer: {
  102. width: width, // Full width for paging
  103. height: '100%',
  104. justifyContent: 'center',
  105. alignItems: 'center',
  106. },
  107. imageBox: {
  108. width: '60%',
  109. height: '60%',
  110. justifyContent: 'center',
  111. alignItems: 'center',
  112. },
  113. image: {
  114. width: '100%',
  115. height: '100%',
  116. },
  117. detailsTag: {
  118. position: 'absolute',
  119. right: 40,
  120. bottom: 80,
  121. width: 100,
  122. height: 60,
  123. justifyContent: 'center',
  124. alignItems: 'center',
  125. },
  126. detailsContent: {
  127. alignItems: 'center',
  128. justifyContent: 'center',
  129. marginTop: 5,
  130. },
  131. levelText: {
  132. color: '#fff',
  133. fontSize: 12,
  134. fontWeight: 'bold',
  135. textShadowColor: '#000',
  136. textShadowOffset: { width: 1, height: 1 },
  137. textShadowRadius: 1,
  138. },
  139. probText: {
  140. color: '#fff',
  141. fontSize: 10,
  142. fontWeight: 'bold',
  143. textShadowColor: '#000',
  144. textShadowOffset: { width: 1, height: 1 },
  145. textShadowRadius: 1,
  146. },
  147. nameTag: {
  148. position: 'absolute',
  149. left: 40,
  150. top: 40,
  151. width: 40,
  152. height: 120,
  153. justifyContent: 'center',
  154. alignItems: 'center',
  155. paddingVertical: 10,
  156. },
  157. nameText: {
  158. color: '#000',
  159. fontSize: 12,
  160. fontWeight: 'bold',
  161. width: 12,
  162. textAlign: 'center',
  163. },
  164. arrow: {
  165. position: 'absolute',
  166. top: '50%',
  167. marginTop: -20,
  168. width: 40,
  169. height: 40,
  170. zIndex: 10,
  171. },
  172. leftArrow: {
  173. left: 10,
  174. },
  175. rightArrow: {
  176. right: 10,
  177. },
  178. arrowImg: {
  179. width: '100%',
  180. height: '100%',
  181. }
  182. });