index.jsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. import styles from "./styles.module.scss";
  2. // import d_bg from "@assets/holidaysGift/d.png";
  3. import img_close from "@assets/holidaysGift/close.png";
  4. // import img_theme from "@assets/holidaysGift/theme.png";
  5. import icon_cash from "@assets/holidaysGift/cash.png";
  6. import icon_gift from "@assets/holidaysGift/gift.png";
  7. // import spinBg from "@assets/holidaysGift/spinBg.png";
  8. import styled from 'styled-components/macro';
  9. import React, { useEffect, useRef, useState } from "react";
  10. import spin_1 from "@assets/wheel/spin-1.png";
  11. import spin_2 from "@assets/wheel/spin-2.png";
  12. import spin_4 from "@assets/wheel/spin-4.png";
  13. import spin_5 from "@assets/wheel/spin-5.png";
  14. // import draw from "@assets/holidaysGift/draw.png";
  15. // import light1 from "@assets/holidaysGift/light1.png";
  16. import light2 from "@assets/holidaysGift/light2.png";
  17. import { useDispatch, useSelector } from "react-redux";
  18. import { getIsHelp, getTurnplate, updateCollect, updateTimes } from "@features/turnplateSlice";
  19. import { formatCash, formatTime, localStorageGet, localStorageSet } from "@utils/helpers";
  20. import { getHolidaysGiftInfo, isLogined, updateHolidaysGiftSpinTimes } from "@features/mainSlice";
  21. import { useNavigate } from "react-router";
  22. import CountUp from "react-countup";
  23. import { Fade, Grow, Modal } from "@mui/material";
  24. import usePaymentActions from "@hooks/usePaymentActions";
  25. import { reqGameInfo, reqHolidaysGiftSpin } from "@features/api";
  26. import HOLIDAYS from "@constants/holidays";
  27. const i18n = global.i18n;
  28. const spinImgSrc = [
  29. null,
  30. spin_1,
  31. spin_2,
  32. null,
  33. spin_4,
  34. spin_5
  35. ];
  36. const spinImg = [];
  37. const turnObj = {
  38. timer: null,
  39. cdTimer: null,
  40. count: 0,
  41. result: null,
  42. resTimer: null,
  43. flashTimer: null,
  44. resetTimer: null,
  45. ishelp: false
  46. };
  47. const CURRENT_HOLIDAY = HOLIDAYS.Christmas;
  48. const getThemeImg = (imgName) => {
  49. const img = require(`@assets/holidaysGift/${CURRENT_HOLIDAY.name}/${imgName}.png`);
  50. return img;
  51. };
  52. const StyledCanvas = styled.canvas`
  53. background-image:url('${getThemeImg("spinBg")}');
  54. `;
  55. const WHolidaysGift = () => {
  56. const info = useSelector(getHolidaysGiftInfo);
  57. // console.error("holidays info", info);
  58. let { slots, user, recharge_rules, status } = info;
  59. const data = slots?.map((item, index) => {
  60. return { id: index + 1, type: 0, value: item };
  61. });
  62. const [spinTimes, setSpinTimes] = useState(user?.left_times);
  63. useEffect(() => {
  64. if (user?.left_times !== undefined) {
  65. setSpinTimes(user.left_times);
  66. }
  67. }, [user]);
  68. const [open, setOpen] = useState(false);
  69. const drawRef = useRef(null);
  70. const dataLen = 16;
  71. const startRotateDegreeDynamic = 3240;
  72. const [drawStyle, setDrawStyle] = useState({});
  73. const navigate = useNavigate();
  74. const dispatch = useDispatch();
  75. const stopduring = 5.5;
  76. const resetduring = 2;
  77. const [collect, setCollect] = useState(0);
  78. const [isLock, setIsLock] = useState(false);
  79. const statusRef = useRef(status);
  80. useEffect(() => {
  81. statusRef.current = status;
  82. }, [status]);
  83. const show = () => {
  84. if (statusRef.current === 0) return global.openNextPopup();;
  85. setOpen(true);
  86. };
  87. const hide = () => {
  88. setOpen(false);
  89. };
  90. const onClose = () => {
  91. hide();
  92. global.openNextPopup();
  93. };
  94. useEffect(() => {
  95. const holidaysGift = {
  96. show,
  97. hide
  98. };
  99. global.holidaysGift = holidaysGift;
  100. }, []);
  101. const onCheckClose = () => {
  102. onClose();
  103. };
  104. const clearCDTimer = () => {
  105. if (turnObj.cdTimer != null) {
  106. clearInterval(turnObj.cdTimer);
  107. turnObj.cdTimer = null;
  108. }
  109. };
  110. const onTurnResult = (res) => {
  111. // console.error("onTurnResult", res);
  112. // return;
  113. if (res.code !== 200) {
  114. resetWheel(0);
  115. return;
  116. }
  117. let { reward, slot_index, left_times } = res.data;
  118. let type = 0;
  119. let curr_amount = 0;
  120. let collect_amount = 0;
  121. let change = 0;
  122. let id = data[slot_index]?.id;
  123. turnObj.result = {
  124. type: type,
  125. reward: id,
  126. amount: collect_amount,
  127. change: change
  128. };
  129. console.error("turnObj.result", turnObj.result);
  130. dispatch(updateHolidaysGiftSpinTimes({ left_times }));
  131. };
  132. const startSpin = async () => {
  133. if (isLock) return;
  134. if (spinTimes <= 0 || isNaN(spinTimes)) return;
  135. // if (spinTimes === 1) isLastSpin = true;
  136. startTurn();
  137. setSpinTimes(spinTimes - 1);
  138. setIsLock(true);
  139. let res = await dispatch(reqHolidaysGiftSpin()).unwrap();
  140. onTurnResult(res);
  141. };
  142. const clearTurnTimer = () => {
  143. if (turnObj.timer != null) {
  144. clearInterval(turnObj.timer);
  145. turnObj.timer = null;
  146. }
  147. };
  148. const startTurn = () => {
  149. clearTurnTimer();
  150. turnObj.count = 0;
  151. let turnCount = turnObj.count;
  152. let during = 0.333;
  153. let roteStyle = {
  154. transform: `rotate(360deg)`,
  155. transition: `transform ${during * 2}s cubic-bezier(0.42, 0.00, 1.00, 1.00)`,
  156. };
  157. setDrawStyle(roteStyle);
  158. let timer = setInterval(() => {
  159. turnCount++;
  160. turnObj.count++;
  161. roteStyle = {
  162. transform: `rotate(${turnCount * 360}deg)`,
  163. transition: `transform ${during}s cubic-bezier(1.00, 1.00, 0.00, 0.00)`,
  164. };
  165. setDrawStyle(roteStyle);
  166. if (turnCount > 2) {
  167. if (turnObj.result) {
  168. stopTurn(turnObj.result.reward);
  169. }
  170. }
  171. }, during * 1000);
  172. turnObj.timer = timer;
  173. };
  174. const stopTurn = (reward) => {
  175. let index = 0;
  176. for (let i = 0; i < dataLen; i++) {
  177. let item = data[i];
  178. if (item && item.id === reward) {
  179. index = i;
  180. break;
  181. }
  182. }
  183. let per = 360 / dataLen;
  184. let angle = turnObj.count * 360 + startRotateDegreeDynamic - (per * (index + 1)) + 11;
  185. drawAnimation(angle);
  186. // console.error("stopTurn ", turnplate);
  187. };
  188. const drawAnimation = (angle = startRotateDegreeDynamic, fix) => {
  189. clearTurnTimer();
  190. let roteStyle = {
  191. transform: "rotate(".concat(angle, "deg)"),
  192. transition: `transform ${stopduring}s cubic-bezier(0.250, 0.460, 0.455, 0.995)`,
  193. };
  194. setDrawStyle(roteStyle);
  195. playRes(angle);
  196. };
  197. const flashDraw = () => {
  198. let show = false;
  199. turnObj.flashTimer = setInterval(() => {
  200. show = !show;
  201. let result = show ? turnObj.result : null;
  202. drawWheel(result);
  203. }, 260);
  204. };
  205. const playRes = (angle = 0) => {
  206. clearResTimer();
  207. let during = (stopduring + 0.5) * 1000;
  208. turnObj.resTimer = setTimeout(() => {
  209. flashDraw();
  210. startReset(angle);
  211. }, during);
  212. };
  213. // const
  214. const clearResTimer = () => {
  215. if (turnObj.resTimer) {
  216. clearTimeout(turnObj.resTimer);
  217. turnObj.resTimer = null;
  218. }
  219. if (turnObj.flashTimer) {
  220. clearInterval(turnObj.flashTimer);
  221. turnObj.flashTimer = null;
  222. }
  223. };
  224. const startReset = (angle = 0) => {
  225. turnObj.resetTimer = setTimeout(() => {
  226. resetWheel(angle);
  227. // console.error("startReset ");
  228. spinOver();
  229. }, resetduring * 1000);
  230. };
  231. const resetWheel = (angle = 0) => {
  232. clearTurnTimer();
  233. turnObj.result = null;
  234. turnObj.count = 0;
  235. turnObj.ishelp = false;
  236. if (angle == null) angle = 0;
  237. else angle = angle % 360;
  238. let roteStyle = {
  239. transform: "rotate(" + angle + "deg)",
  240. transition: ``,
  241. };
  242. setDrawStyle(roteStyle);
  243. drawWheel(null);
  244. setCollect(0);
  245. setIsLock(false);
  246. clearResTimer();
  247. };
  248. const clearResetTimer = () => {
  249. if (turnObj.resetTimer) {
  250. clearTimeout(turnObj.resetTimer);
  251. turnObj.resetTimer = null;
  252. }
  253. };
  254. const drawWheel2 = (result) => {
  255. let draw = drawRef.current;
  256. if (draw == null) return;
  257. let colors = ["#FFFFFF", "#8960DD"];
  258. let color_len = colors.length;
  259. let fontSizeNum = 14;
  260. if (data != null) {
  261. let s = draw;
  262. let rdat = data.slice(0, dataLen);
  263. let c = draw.getContext("2d");
  264. let dcwidth = draw.clientWidth;
  265. let dcheight = draw.clientHeight;
  266. let devicePixelRatioNum = window.devicePixelRatio < 1 ? 1 : window.devicePixelRatio;
  267. s.width = dcwidth * devicePixelRatioNum;
  268. s.height = dcheight * devicePixelRatioNum;
  269. c.clearRect(0, 0, s.width, s.height);
  270. let l = 2 * Math.PI / dataLen;
  271. let g = -s.width / 2 + s.width / 2 / 2.5;
  272. c.translate(s.width / 2, s.width / 2);
  273. c.rotate(-3 * l);
  274. c.save();
  275. for (let i = 0; i < dataLen; i++) {
  276. let item = rdat[i];
  277. if (item == null) continue;
  278. let color = colors[i % color_len];
  279. if (result) {
  280. if (result.reward === item.id) {
  281. c.beginPath();
  282. c.moveTo(0, 0);
  283. c.arc(0, 0, s.width / 2 - 6, -l, 0);
  284. c.strokeStyle = "#fff";
  285. c.fillStyle = "#fff";
  286. c.lineWidth = 10;
  287. c.lineTo(0, -5);
  288. c.stroke();
  289. c.closePath();
  290. }
  291. }
  292. c.rotate(l);
  293. c.save();
  294. c.beginPath();
  295. c.fillStyle = color;
  296. c.textBaseline = "middle";
  297. c.textAlign = "center";
  298. c.font = "".concat(devicePixelRatioNum * fontSizeNum, "px SWISSC-BT");
  299. c.translate(s.width / 5.2, -s.width / dataLen);
  300. c.rotate(1);
  301. c.translate(-s.width / 5.2, s.width / dataLen);
  302. if (item.type === 0) {
  303. c.fillText(item.value, s.width / 7.2, g + devicePixelRatioNum * fontSizeNum * .36, 3 * s.width / dataLen);
  304. }
  305. else {
  306. let img = spinImg[item.type];
  307. if (img) c.drawImage(img, s.width / 23, g - 11 * devicePixelRatioNum, s.width / 5, s.width / 4.37);
  308. }
  309. c.restore();
  310. }
  311. }
  312. };
  313. const drawWheel = (result) => {
  314. let draw = drawRef.current;
  315. if (draw == null) return;
  316. let colors = CURRENT_HOLIDAY.gift.color;
  317. let color_len = colors.length;
  318. let fontSizeNum = 18;
  319. if (data != null) {
  320. let s = draw;
  321. let rdat = data.slice(0, dataLen);
  322. let c = draw.getContext("2d");
  323. let dcwidth = draw.clientWidth;
  324. let dcheight = draw.clientHeight;
  325. let devicePixelRatioNum = window.devicePixelRatio < 1 ? 1 : window.devicePixelRatio;
  326. s.width = dcwidth * devicePixelRatioNum;
  327. s.height = dcheight * devicePixelRatioNum;
  328. c.clearRect(0, 0, s.width, s.height);
  329. let l = 2 * Math.PI / dataLen;
  330. let g = -s.width / 2 + s.width / 2 / 2.5;
  331. c.translate(s.width / 2, s.width / 2);
  332. c.rotate(-1 * l);
  333. c.save();
  334. for (let i = 0; i < dataLen; i++) {
  335. let item = rdat[i];
  336. if (item == null) continue;
  337. let color = colors[i % color_len];
  338. if (result) {
  339. if (result.reward === item.id) {
  340. c.beginPath();
  341. c.moveTo(0, 0);
  342. c.arc(0, 0, s.width / 2 - 6, -3 * l, -2 * l);
  343. c.strokeStyle = "#fff";
  344. c.fillStyle = "#fff";
  345. c.lineWidth = 3.3 * devicePixelRatioNum;
  346. c.lineTo(0, -5);
  347. c.stroke();
  348. c.closePath();
  349. }
  350. }
  351. c.rotate(l);
  352. c.save();
  353. c.beginPath();
  354. c.fillStyle = color;
  355. c.textBaseline = "middle";
  356. c.textAlign = "center";
  357. c.font = "".concat(devicePixelRatioNum * fontSizeNum, "px Roboto");
  358. c.translate(s.width / 5.2, -s.width / dataLen);
  359. c.rotate(1);
  360. c.translate(-s.width / 5.2, s.width / dataLen);
  361. if (item.type === 0) {
  362. c.rotate(4);
  363. c.fillText("$" + item.value, 50 * devicePixelRatioNum, -0.0);
  364. }
  365. else {
  366. let img = spinImg[item.type];
  367. if (img) c.drawImage(img, s.width / 23, g - 11 * devicePixelRatioNum, s.width / 5, s.width / 4.37);
  368. }
  369. c.restore();
  370. }
  371. }
  372. };
  373. useEffect(() => {
  374. return () => {
  375. clearTurnTimer();
  376. clearCDTimer();
  377. clearResTimer();
  378. turnObj.result = null;
  379. clearResetTimer();
  380. };
  381. }, []);
  382. // 检测 Modal 内容是否已渲染
  383. const [contentMounted, setContentMounted] = useState(false);
  384. useEffect(() => {
  385. if (open) {
  386. // 等待下一帧确保 DOM 已渲染
  387. requestAnimationFrame(() => {
  388. requestAnimationFrame(() => {
  389. setContentMounted(true);
  390. });
  391. });
  392. } else {
  393. setContentMounted(false);
  394. }
  395. }, [open]);
  396. useEffect(() => {
  397. if (contentMounted && drawRef.current) {
  398. drawWheel();
  399. }
  400. }, [contentMounted]);
  401. // let isLastSpin = false;
  402. const spinOver = () => {
  403. dispatch(reqGameInfo());
  404. global.showDropCoins();
  405. };
  406. const renderSpinInfo = () => {
  407. return (
  408. <>
  409. <div className={styles.spin_con}>
  410. <button className={styles.spin_btn} onClick={() => {
  411. startSpin();
  412. }}>
  413. {spinTimes > 0 && !isLock ? <img src={light2} className={`${styles.light2} ${styles.scale}`} /> : <></>}
  414. <img src={getThemeImg("draw")} className={styles.spin_img} />
  415. {spinTimes > 0 && !isLock ? <img src={getThemeImg("light1")} className={`${styles.light1} ${styles.blink}`} /> : <></>}
  416. <div className={styles.spin_content}>
  417. {
  418. collect > 0 ? (
  419. <CountUp
  420. start={0}
  421. end={Number(formatCash(collect))}
  422. duration={1}
  423. separator=" "
  424. decimals={2}
  425. prefix={"+"}
  426. >
  427. {({ countUpRef, start }) => {
  428. return (
  429. <div ref={(ref) => {
  430. countUpRef.current = ref;
  431. if (countUpRef.collect !== collect) {
  432. countUpRef.collect = collect;
  433. start();
  434. }
  435. }} className={styles.collect_num} />
  436. );
  437. }}
  438. </CountUp>
  439. ) : (
  440. <div className={styles.spin_num}>{spinTimes}</div>
  441. )
  442. }
  443. </div>
  444. </button>
  445. </div>
  446. </>
  447. );
  448. };
  449. const { startPayingOrder } = usePaymentActions();
  450. const handleBuyClick = (rule) => {
  451. setOpen(false);
  452. let payload = {
  453. price: rule.amount,
  454. goodsName: "",
  455. payType: "",
  456. GiftsID: rule.gear.gift_id,
  457. gear: rule.gear.gear
  458. };
  459. startPayingOrder(payload);
  460. localStorageSet("payHolidaysGift", true);
  461. };
  462. if (!!!status) return <></>;
  463. return (
  464. <>
  465. <Modal open={open} onClose={onCheckClose} closeAfterTransition>
  466. <Fade in={open} timeout={500}>
  467. <div className={`${styles.container} ${styles[CURRENT_HOLIDAY.name]}`}>
  468. {/* <Grow in={open} timeout={500}> */}
  469. <div className={styles.wheel}>
  470. <div className={`${styles.wheelBox}`}>
  471. <img src={getThemeImg("bg")} className={styles.bg} />
  472. <div className={styles.drawContent}>
  473. <StyledCanvas ref={drawRef} className={styles.draw} style={drawStyle} />
  474. </div>
  475. {
  476. CURRENT_HOLIDAY.gift.decorate ? <img src={getThemeImg("decorate")} className={styles.decorate} /> : <></>
  477. }
  478. {renderSpinInfo()}
  479. </div>
  480. <img src={getThemeImg("theme")} alt="" className={styles.themeImg} />
  481. <div className={styles.msgPanel}>
  482. <div className={styles.title}>
  483. Complete The Recharge And Draw A Prize To GetUp To 100% Extra Reward
  484. </div>
  485. {
  486. recharge_rules?.map((rule, index) => {
  487. return (
  488. <div className={styles.item} key={index}>
  489. <div className={styles.itemBox}>
  490. <img src={icon_cash} alt="" className={styles.icon_cash} />
  491. <span className={styles.price}>${rule?.amount}</span>
  492. </div>
  493. <span className={styles.plus}>+</span>
  494. <div className={styles.itemBox}>
  495. <img src={icon_gift} alt="" className={styles.icon_gift} />
  496. <span className={styles.spinTimes}>SPIN+{rule?.times}</span>
  497. </div>
  498. <div className={styles.buy} onClick={() => handleBuyClick(rule)} >${rule?.gear?.money}</div>
  499. </div>
  500. );
  501. })
  502. }
  503. </div>
  504. </div>
  505. {/* </Grow> */}
  506. <img src={img_close} alt="" className={styles.close} onClick={onCheckClose} />
  507. </div>
  508. </Fade >
  509. </Modal >
  510. </>
  511. );
  512. };
  513. export default WHolidaysGift;