LotteryResultModal.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. import { AVPlaybackStatus, ResizeMode, Video } from 'expo-av';
  2. import { Image } from 'expo-image';
  3. import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
  4. import {
  5. ActivityIndicator,
  6. Alert,
  7. Animated,
  8. Dimensions,
  9. ImageBackground,
  10. Modal,
  11. ScrollView,
  12. StyleSheet,
  13. Text,
  14. TouchableOpacity,
  15. View
  16. } from 'react-native';
  17. import { useSafeAreaInsets } from 'react-native-safe-area-context';
  18. import { convertApply, getApplyResult } from '@/services/award';
  19. const { width: SCREEN_WIDTH } = Dimensions.get('window');
  20. const CARD_WIDTH = (SCREEN_WIDTH - 60) / 3;
  21. const CARD_HEIGHT = CARD_WIDTH * 1.5;
  22. const CDN_BASE = 'https://cdn.acetoys.cn';
  23. const imgUrl = `${CDN_BASE}/kai_xin_ma_te/supermart`;
  24. const imgUrlSupermart = `${CDN_BASE}/supermart`;
  25. const LEVEL_MAP: Record<string, { title: string; color: string; rgba: string; resultBg: string; borderImg: string }> = {
  26. A: {
  27. title: '超神款',
  28. color: '#F62C71',
  29. rgba: 'rgba(246, 44, 113, 1)',
  30. resultBg: `${imgUrlSupermart}/supermart/box/resultBgA.png`,
  31. borderImg: `${imgUrlSupermart}/supermart/box/borderImgA.png`
  32. },
  33. B: {
  34. title: '欧皇款',
  35. color: '#E9C525',
  36. rgba: 'rgba(233,197,37, 1)',
  37. resultBg: `${imgUrlSupermart}/supermart/box/resultBgB.png`,
  38. borderImg: `${imgUrlSupermart}/supermart/box/borderImgB.png`
  39. },
  40. C: {
  41. title: '隐藏款',
  42. color: '#A72CF6',
  43. rgba: 'rgba(167, 44, 246, 1)',
  44. resultBg: `${imgUrlSupermart}/supermart/box/resultBgC.png`,
  45. borderImg: `${imgUrlSupermart}/supermart/box/borderImgC.png`
  46. },
  47. D: {
  48. title: '普通款',
  49. color: '#40c9d7',
  50. rgba: 'rgba(64, 201, 215, 1)',
  51. resultBg: `${imgUrlSupermart}/supermart/box/resultBgD.png`,
  52. borderImg: `${imgUrlSupermart}/supermart/box/borderImgD.png`
  53. },
  54. };
  55. const LotteryImages = {
  56. lotteryBg: `${imgUrlSupermart}/supermart/box/sequence/sequence0.jpg`,
  57. cardBack: `${imgUrl}/box/back1.png`,
  58. halo: `${imgUrlSupermart}/supermart/box/halo.gif`,
  59. levelA_bg: `${imgUrlSupermart}/supermart/box/levelD.png`, // Using levelD as placeholder if specific not found, or specific bg
  60. levelA_title: `${imgUrlSupermart}/supermart/box/detail/levelTextA.png`,
  61. close: `${imgUrlSupermart}/supermart/box/qiji_close.png`,
  62. };
  63. const DEFAULT_JACKPOT_VIDEO = 'https://cdn.acetoys.cn/kai_xin_ma_te/supermart/box/lottery/jackpot.mp4';
  64. const KingModal = ({ visible, data, onClose }: { visible: boolean; data: LotteryItem | null; onClose: () => void }) => {
  65. if (!visible || !data) return null;
  66. return (
  67. <Modal visible={visible} transparent animationType="fade" onRequestClose={onClose}>
  68. <View style={styles.kingContainer}>
  69. <View style={styles.kingMask}>
  70. <TouchableOpacity style={StyleSheet.absoluteFill} onPress={onClose} />
  71. </View>
  72. <View style={styles.kingWrapper}>
  73. <Image
  74. source={{ uri: LEVEL_MAP[data.level]?.resultBg || LEVEL_MAP.A.resultBg }}
  75. style={styles.kingBg}
  76. contentFit="fill"
  77. />
  78. <Animated.Image
  79. source={{ uri: data.cover }}
  80. style={styles.kingProduct}
  81. resizeMode="contain"
  82. />
  83. <Image
  84. source={{ uri: LotteryImages.levelA_title }}
  85. style={styles.kingTitle}
  86. contentFit="contain"
  87. />
  88. </View>
  89. <TouchableOpacity style={styles.kingCloseBtn} onPress={onClose}>
  90. <Image source={{ uri: LotteryImages.close }} style={styles.kingCloseIcon} />
  91. </TouchableOpacity>
  92. </View>
  93. </Modal>
  94. );
  95. };
  96. interface LotteryItem {
  97. id: string;
  98. name: string;
  99. cover: string;
  100. level: string;
  101. magicAmount?: number;
  102. spu?: { marketPrice: number };
  103. }
  104. export interface LotteryResultModalRef {
  105. show: (tradeNo: string) => void;
  106. close: () => void;
  107. }
  108. interface LotteryResultModalProps {
  109. onClose?: () => void;
  110. onGoStore?: () => void;
  111. }
  112. export const LotteryResultModal = forwardRef<LotteryResultModalRef, LotteryResultModalProps>(
  113. ({ onClose, onGoStore }, ref) => {
  114. const insets = useSafeAreaInsets();
  115. const [visible, setVisible] = useState(false);
  116. const [loading, setLoading] = useState(true);
  117. const [tableData, setTableData] = useState<LotteryItem[]>([]);
  118. const [total, setTotal] = useState(0);
  119. const [showResult, setShowResult] = useState(false);
  120. const [showDh, setShowDh] = useState(false);
  121. const [haloShow, setHaloShow] = useState(false);
  122. const [rebateAmount, setRebateAmount] = useState(0);
  123. const [animationEnabled, setAnimationEnabled] = useState(true);
  124. const [isSkip, setIsSkip] = useState(true);
  125. const [tradeNo, setTradeNo] = useState('');
  126. const [kingVisible, setKingVisible] = useState(false);
  127. const [kingData, setKingData] = useState<LotteryItem | null>(null);
  128. const [videoVisible, setVideoVisible] = useState(false);
  129. const [videoUrl, setVideoUrl] = useState('');
  130. const videoRef = useRef<Video>(null);
  131. const flipAnims = useRef<Animated.Value[]>([]);
  132. const startFlow = (data: LotteryItem[]) => {
  133. setVideoVisible(false);
  134. const levelAItem = data.find((item: LotteryItem) => ['A', 'a'].includes(item.level));
  135. if (levelAItem) {
  136. setKingData(levelAItem);
  137. setKingVisible(true);
  138. } else {
  139. setTimeout(() => flipCards(data), 500);
  140. }
  141. };
  142. const dataLoadedRef = useRef(false);
  143. useImperativeHandle(ref, () => ({
  144. show: (tNo: string) => {
  145. setTradeNo(tNo);
  146. setVisible(true);
  147. setLoading(true);
  148. setTableData([]);
  149. setTotal(0);
  150. setShowResult(false);
  151. setShowDh(false);
  152. setHaloShow(false);
  153. setRebateAmount(0);
  154. setIsSkip(true);
  155. setKingVisible(false);
  156. setKingData(null);
  157. setVideoVisible(false);
  158. setVideoUrl('');
  159. dataLoadedRef.current = false;
  160. flipAnims.current = [];
  161. },
  162. close: () => {
  163. setVisible(false);
  164. dataLoadedRef.current = false;
  165. },
  166. }));
  167. const flipCards = (data: LotteryItem[]) => {
  168. setIsSkip(false);
  169. setHaloShow(true);
  170. setTimeout(() => setHaloShow(false), 1000);
  171. const maxCards = Math.min(data.length, 9);
  172. const animations = flipAnims.current.slice(0, maxCards).map((anim, index) => {
  173. return Animated.sequence([
  174. Animated.delay(index * 200),
  175. Animated.timing(anim, { toValue: 1, duration: 200, useNativeDriver: true }),
  176. ]);
  177. });
  178. Animated.parallel(animations).start(() => {
  179. setTimeout(() => {
  180. setShowDh(data.every((item) => item.level !== 'B' && item.level !== 'A'));
  181. setShowResult(true);
  182. }, 900);
  183. });
  184. };
  185. useEffect(() => {
  186. if (!visible || !tradeNo || dataLoadedRef.current) return;
  187. let attempts = 0;
  188. const maxAttempts = 13;
  189. let timeoutId: ReturnType<typeof setTimeout>;
  190. let isMounted = true;
  191. const fetchData = async () => {
  192. if (!isMounted) return;
  193. try {
  194. const res = await getApplyResult(tradeNo);
  195. if (!isMounted) return;
  196. if (res?.inventoryList && res.inventoryList.length > 0) {
  197. dataLoadedRef.current = true;
  198. if (res.rebateAmount) setRebateAmount(res.rebateAmount);
  199. let array = res.inventoryList;
  200. if (res.magicFireworksList && res.magicFireworksList.length > 0) {
  201. array = [...res.magicFireworksList, ...res.inventoryList];
  202. }
  203. flipAnims.current = array.map(() => new Animated.Value(0));
  204. const sum = array.reduce((acc: number, item: LotteryItem) => acc + (item.magicAmount || 0), 0);
  205. setTotal(sum);
  206. setTableData(array);
  207. setLoading(false);
  208. setLoading(false);
  209. // Determine playing video
  210. let playVideoUrl = '';
  211. if (res.video) {
  212. playVideoUrl = res.video;
  213. } else {
  214. // Check if any Level A item exists (Robust check)
  215. const hasLevelA = array.some((item: LotteryItem) => ['A', 'a'].includes(item.level));
  216. if (hasLevelA) {
  217. playVideoUrl = DEFAULT_JACKPOT_VIDEO;
  218. }
  219. }
  220. if (playVideoUrl) {
  221. setVideoUrl(playVideoUrl);
  222. setVideoVisible(true);
  223. } else {
  224. startFlow(array);
  225. }
  226. } else if (attempts < maxAttempts) {
  227. attempts++;
  228. timeoutId = setTimeout(fetchData, 400);
  229. } else {
  230. setLoading(false);
  231. Alert.alert('提示', '获取结果超时,请在仓库中查看');
  232. }
  233. } catch (error) {
  234. console.error(error);
  235. if (attempts < maxAttempts) {
  236. attempts++;
  237. timeoutId = setTimeout(fetchData, 400);
  238. } else {
  239. setLoading(false);
  240. }
  241. }
  242. };
  243. fetchData();
  244. return () => {
  245. isMounted = false;
  246. if (timeoutId) clearTimeout(timeoutId);
  247. };
  248. }, [visible, tradeNo]);
  249. const handleSkip = () => {
  250. setIsSkip(false);
  251. flipAnims.current.forEach((anim) => anim.setValue(1));
  252. setShowDh(tableData.every((item) => item.level !== 'B' && item.level !== 'A'));
  253. setShowResult(true);
  254. };
  255. const handleDhAll = async () => {
  256. if (!total) return;
  257. try {
  258. const ids = tableData.filter((item) => item.magicAmount).map((item) => item.id);
  259. const res = await convertApply(ids);
  260. if (res) {
  261. setTableData((prev) => prev.map((item) => ({ ...item, magicAmount: 0 })));
  262. setTotal(0);
  263. setTotal(0);
  264. Alert.alert('提示', '兑换成功');
  265. }
  266. } catch {
  267. Alert.alert('提示', '兑换失败,请重试');
  268. }
  269. };
  270. const handleBack = () => {
  271. setVisible(false);
  272. onClose?.();
  273. };
  274. const handleGoStore = () => {
  275. setVisible(false);
  276. onGoStore?.();
  277. };
  278. const renderCard = (item: LotteryItem, index: number) => {
  279. const levelConfig = LEVEL_MAP[item.level] || LEVEL_MAP.D;
  280. const flipAnim = flipAnims.current[index] || new Animated.Value(1);
  281. const isFirst9 = index < 9;
  282. // 前9张卡片有翻转动画
  283. if (isFirst9) {
  284. const backRotate = flipAnim.interpolate({
  285. inputRange: [0, 1],
  286. outputRange: ['0deg', '90deg']
  287. });
  288. const frontRotate = flipAnim.interpolate({
  289. inputRange: [0, 1],
  290. outputRange: ['-90deg', '0deg']
  291. });
  292. const backOpacity = flipAnim.interpolate({
  293. inputRange: [0, 0.5, 1],
  294. outputRange: [1, 0, 0]
  295. });
  296. const frontOpacity = flipAnim.interpolate({
  297. inputRange: [0, 0.5, 1],
  298. outputRange: [0, 0, 1]
  299. });
  300. return (
  301. <View key={item.id || index} style={styles.cardWrapper}>
  302. {/* 背面 - 卡牌背面 */}
  303. <Animated.View style={[styles.cardBack, { transform: [{ rotateY: backRotate }], opacity: backOpacity }]}>
  304. <Image source={{ uri: LotteryImages.cardBack }} style={styles.cardBackImage} contentFit="cover" />
  305. </Animated.View>
  306. {/* 正面 - 商品信息 */}
  307. <Animated.View style={[styles.cardFront, { transform: [{ rotateY: frontRotate }], opacity: frontOpacity }]}>
  308. <ImageBackground source={{ uri: levelConfig.resultBg }} style={styles.cardFrontBg} resizeMode="cover">
  309. <Image source={{ uri: item.cover }} style={styles.productImage} contentFit="contain" />
  310. <Image source={{ uri: levelConfig.borderImg }} style={styles.borderImage} contentFit="cover" />
  311. <View style={styles.cardInfo}>
  312. <View style={styles.infoRow}>
  313. <Text style={styles.levelText}>{levelConfig.title}</Text>
  314. <Text style={styles.priceText}>¥{item.spu?.marketPrice || 0}</Text>
  315. </View>
  316. <View style={styles.exchangeRow}>
  317. <Text style={styles.exchangeText}>价值:{item.magicAmount || 0}果实</Text>
  318. </View>
  319. <Text style={styles.nameText} numberOfLines={1}>{item.name}</Text>
  320. </View>
  321. </ImageBackground>
  322. </Animated.View>
  323. </View>
  324. );
  325. }
  326. // 9张以后的卡片直接显示正面
  327. return (
  328. <View key={item.id || index} style={styles.cardWrapper}>
  329. <View style={styles.cardFrontStatic}>
  330. <ImageBackground source={{ uri: levelConfig.resultBg }} style={styles.cardFrontBg} resizeMode="cover">
  331. <Image source={{ uri: item.cover }} style={styles.productImage} contentFit="contain" />
  332. <Image source={{ uri: levelConfig.borderImg }} style={styles.borderImage} contentFit="cover" />
  333. <View style={styles.cardInfo}>
  334. <View style={styles.infoRow}>
  335. <Text style={styles.levelText}>{levelConfig.title}</Text>
  336. <Text style={styles.priceText}>¥{item.spu?.marketPrice || 0}</Text>
  337. </View>
  338. <View style={styles.exchangeRow}>
  339. <Text style={styles.exchangeText}>价值:{item.magicAmount || 0}果实</Text>
  340. </View>
  341. <Text style={styles.nameText} numberOfLines={1}>{item.name}</Text>
  342. </View>
  343. </ImageBackground>
  344. </View>
  345. </View>
  346. );
  347. };
  348. return (
  349. <Modal visible={visible} transparent animationType="fade" onRequestClose={handleBack}>
  350. <View style={styles.container}>
  351. <ImageBackground source={{ uri: LotteryImages.lotteryBg }} style={styles.background} resizeMode="cover">
  352. {/* 光晕效果 */}
  353. {haloShow && (
  354. <View style={styles.haloSection}>
  355. <Image source={{ uri: LotteryImages.halo }} style={styles.halo} contentFit="cover" />
  356. </View>
  357. )}
  358. {/* 标题 */}
  359. <View style={[styles.titleSection, { marginTop: insets.top + 40 }]}>
  360. <Text style={styles.titleText}>恭喜您获得</Text>
  361. </View>
  362. {/* 主内容区 */}
  363. <View style={styles.mainSection}>
  364. {loading ? (
  365. <View style={styles.loadingBox}>
  366. <ActivityIndicator size="large" color="#fff" />
  367. <Text style={styles.loadingText}>正在开启宝箱...</Text>
  368. </View>
  369. ) : (
  370. <ScrollView style={styles.cardList} showsVerticalScrollIndicator={false}>
  371. <View style={styles.cardGrid}>
  372. {tableData.map((item, index) => renderCard(item, index))}
  373. </View>
  374. </ScrollView>
  375. )}
  376. </View>
  377. {/* 底部按钮区 */}
  378. {showResult && (
  379. <View style={[styles.bottomSection, { paddingBottom: insets.bottom + 20 }]}>
  380. <View style={styles.bottomBtns}>
  381. {total > 0 && showDh && (
  382. <TouchableOpacity style={styles.dhBtn} onPress={handleDhAll}>
  383. <Text style={styles.dhBtnText}>全部兑换</Text>
  384. <Text style={styles.dhBtnSubText}>共兑换果实 {total}</Text>
  385. </TouchableOpacity>
  386. )}
  387. <TouchableOpacity style={styles.againBtn} onPress={handleBack}>
  388. <Text style={styles.againBtnText}>再来一发</Text>
  389. </TouchableOpacity>
  390. </View>
  391. <TouchableOpacity style={styles.storeLink} onPress={handleGoStore}>
  392. <Text style={styles.storeLinkText}>前往 <Text style={styles.storeHighlight}>仓库</Text> 查看</Text>
  393. </TouchableOpacity>
  394. {rebateAmount > 0 && (
  395. <Text style={styles.rebateText}>本次支付返还果实 <Text style={styles.rebateAmount}>{rebateAmount}</Text> 枚</Text>
  396. )}
  397. <View style={styles.animationSwitch}>
  398. <Text style={styles.switchLabel}>是否开启动画</Text>
  399. <TouchableOpacity
  400. style={[styles.switchBtn, animationEnabled && styles.switchBtnActive]}
  401. onPress={() => setAnimationEnabled(!animationEnabled)}
  402. >
  403. <View style={[styles.switchThumb, animationEnabled && styles.switchThumbActive]} />
  404. </TouchableOpacity>
  405. </View>
  406. </View>
  407. )}
  408. {/* 跳过动画按钮 */}
  409. {isSkip && !loading && (
  410. <TouchableOpacity style={styles.skipBtn} onPress={handleSkip}>
  411. <Text style={styles.skipText}>跳过动画</Text>
  412. </TouchableOpacity>
  413. )}
  414. </ImageBackground>
  415. </View>
  416. <KingModal
  417. visible={kingVisible}
  418. data={kingData}
  419. onClose={() => {
  420. setKingVisible(false);
  421. setTimeout(() => flipCards(tableData), 300);
  422. }}
  423. />
  424. {videoVisible && videoUrl ? (
  425. <View style={styles.videoContainer}>
  426. <Video
  427. key={videoUrl}
  428. ref={videoRef}
  429. source={{ uri: videoUrl }}
  430. style={styles.video}
  431. resizeMode={ResizeMode.COVER}
  432. shouldPlay
  433. isLooping={false}
  434. useNativeControls={false}
  435. onLoad={() => {
  436. videoRef.current?.playAsync();
  437. }}
  438. onError={(error) => {
  439. console.error('Video playback error:', error);
  440. setVideoVisible(false);
  441. startFlow(tableData);
  442. }}
  443. onPlaybackStatusUpdate={(status: AVPlaybackStatus) => {
  444. if (!status.isLoaded) return;
  445. if (status.didJustFinish) {
  446. setVideoVisible(false);
  447. startFlow(tableData);
  448. }
  449. }}
  450. />
  451. <TouchableOpacity
  452. style={styles.skipVideoBtn}
  453. onPress={() => {
  454. setVideoVisible(false);
  455. startFlow(tableData);
  456. }}
  457. >
  458. <Text style={styles.skipText}>跳过</Text>
  459. </TouchableOpacity>
  460. </View>
  461. ) : null}
  462. </Modal>
  463. );
  464. }
  465. );
  466. const styles = StyleSheet.create({
  467. container: { flex: 1, backgroundColor: '#1a1a2e' },
  468. background: { flex: 1 },
  469. haloSection: { position: 'absolute', left: 0, top: 0, right: 0, bottom: 0, zIndex: 9999 },
  470. halo: { width: '100%', height: '100%' },
  471. titleSection: { alignItems: 'center', marginBottom: 15 },
  472. titleText: {
  473. fontSize: 31,
  474. fontWeight: 'bold',
  475. color: '#fffecc',
  476. textShadowColor: '#a06939',
  477. textShadowOffset: { width: 1, height: 1 },
  478. textShadowRadius: 2
  479. },
  480. mainSection: { flex: 1, paddingTop: 20 },
  481. loadingBox: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  482. loadingText: { marginTop: 15, fontSize: 14, color: '#fff' },
  483. cardList: { flex: 1 },
  484. cardGrid: {
  485. flexDirection: 'row',
  486. flexWrap: 'wrap',
  487. paddingHorizontal: 15,
  488. justifyContent: 'flex-start'
  489. },
  490. cardWrapper: {
  491. width: CARD_WIDTH,
  492. height: CARD_HEIGHT,
  493. marginHorizontal: 5,
  494. marginBottom: 15,
  495. position: 'relative'
  496. },
  497. cardBack: {
  498. position: 'absolute',
  499. width: '100%',
  500. height: '100%',
  501. backfaceVisibility: 'hidden'
  502. },
  503. cardBackImage: { width: '100%', height: '100%', borderRadius: 10 },
  504. cardFront: {
  505. position: 'absolute',
  506. width: '100%',
  507. height: '100%',
  508. backfaceVisibility: 'hidden',
  509. borderRadius: 10,
  510. overflow: 'hidden'
  511. },
  512. cardFrontStatic: {
  513. width: '100%',
  514. height: '100%',
  515. borderRadius: 10,
  516. overflow: 'hidden'
  517. },
  518. cardFrontBg: {
  519. width: '100%',
  520. height: '100%',
  521. paddingTop: 15,
  522. borderRadius: 10,
  523. overflow: 'hidden'
  524. },
  525. productImage: { width: '85%', height: '55%', alignSelf: 'center' },
  526. borderImage: { position: 'absolute', left: 0, top: 0, width: '100%', height: '100%' },
  527. cardInfo: { position: 'absolute', left: 0, right: 0, bottom: 7, paddingHorizontal: 10 },
  528. infoRow: {
  529. flexDirection: 'row',
  530. justifyContent: 'space-between',
  531. alignItems: 'center',
  532. marginBottom: 2
  533. },
  534. levelText: { fontSize: 13, fontWeight: 'bold', color: '#fff' },
  535. priceText: { fontSize: 12, fontWeight: 'bold', color: '#fff' },
  536. exchangeRow: { marginBottom: 2 },
  537. exchangeText: { fontSize: 10, color: '#fff', fontWeight: 'bold' },
  538. nameText: { fontSize: 12, fontWeight: 'bold', color: '#fff' },
  539. bottomSection: { paddingHorizontal: 20, paddingTop: 20 },
  540. bottomBtns: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center' },
  541. dhBtn: {
  542. backgroundColor: '#fff7e3',
  543. borderRadius: 20,
  544. paddingHorizontal: 18,
  545. paddingVertical: 8,
  546. marginRight: 10,
  547. alignItems: 'center'
  548. },
  549. dhBtnText: { fontSize: 14, fontWeight: '500', color: '#000' },
  550. dhBtnSubText: { fontSize: 10, color: '#735200' },
  551. againBtn: {
  552. backgroundColor: '#fec433',
  553. borderRadius: 20,
  554. paddingHorizontal: 25,
  555. paddingVertical: 12
  556. },
  557. againBtnText: { fontSize: 14, fontWeight: '600', color: '#000' },
  558. storeLink: { alignItems: 'center', marginTop: 12 },
  559. storeLinkText: { fontSize: 12, color: '#fff' },
  560. storeHighlight: { color: '#ff9600' },
  561. rebateText: { textAlign: 'center', marginTop: 13, fontSize: 13, color: '#fff' },
  562. rebateAmount: { color: '#ffeb3b', fontSize: 14, fontWeight: 'bold' },
  563. animationSwitch: {
  564. flexDirection: 'row',
  565. justifyContent: 'center',
  566. alignItems: 'center',
  567. marginTop: 8
  568. },
  569. switchLabel: { fontSize: 12, color: '#dedede', marginRight: 10 },
  570. switchBtn: {
  571. width: 44,
  572. height: 24,
  573. borderRadius: 12,
  574. backgroundColor: '#666',
  575. justifyContent: 'center',
  576. paddingHorizontal: 2
  577. },
  578. switchBtnActive: { backgroundColor: '#ff9600' },
  579. switchThumb: { width: 20, height: 20, borderRadius: 10, backgroundColor: '#fff' },
  580. switchThumbActive: { alignSelf: 'flex-end' },
  581. skipBtn: {
  582. position: 'absolute',
  583. bottom: '10%',
  584. alignSelf: 'center',
  585. backgroundColor: 'rgba(0,0,0,0.4)',
  586. paddingHorizontal: 15,
  587. paddingVertical: 7,
  588. borderRadius: 15
  589. },
  590. skipText: { fontSize: 14, color: '#fff' },
  591. kingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'transparent' },
  592. kingMask: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.7)' },
  593. kingWrapper: { width: 353 * 1.2, height: 545 * 1.2, alignItems: 'center', justifyContent: 'center' },
  594. kingBg: { position: 'absolute', width: '100%', height: '100%' },
  595. kingProduct: { width: 293 * 1.2, height: 370 * 1.2, marginTop: 33, borderRadius: 8 },
  596. kingTitle: { position: 'absolute', bottom: 40, width: 230 * 1.2, height: 102 * 1.2 },
  597. kingCloseBtn: { marginTop: 30 },
  598. kingCloseIcon: { width: 60, height: 60 },
  599. videoContainer: { ...StyleSheet.absoluteFillObject, zIndex: 10000, backgroundColor: 'black' },
  600. video: { width: '100%', height: '100%' },
  601. skipVideoBtn: { position: 'absolute', top: 60, right: 20, backgroundColor: 'rgba(0,0,0,0.5)', paddingHorizontal: 15, paddingVertical: 8, borderRadius: 20 },
  602. });