WWheel.jsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import styles from "./styles.module.scss";
  2. import d_bg from "@assets/wheel/d.png";
  3. import bg_light from "@assets/wheel/bg_light.png";
  4. import light1 from "@assets/wheel/light1.png";
  5. import light2 from "@assets/wheel/light2.png";
  6. import spinBg from "@assets/wheel/spinBg.png";
  7. import pointer from "@assets/wheel/pointer.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/wheel/draw.png";
  15. import {useDispatch, useSelector} from "react-redux";
  16. import {getIsHelp, getTurnplate, updateCollect, updateTimes} from "@features/turnplateSlice";
  17. import {formatCash, formatTime, localStorageGet} from "@utils/helpers";
  18. import SvgIcon from "@components/SvgIcon";
  19. import {ReactComponent as ShareIcon} from '@assets/singlesvg/share.svg';
  20. import {isLogined} from "@features/mainSlice";
  21. import {useNavigate} from "react-router";
  22. import {ROUTE_PATH} from "@constants/routes";
  23. import {reqTurnplateHelp, reqTurnplateKan} from "@features/api";
  24. import CountUp from "react-countup";
  25. import WPopInfo from "@widgets/WCashWheel/WPopInfo/WPopInfo";
  26. import {LOCAL_WHEEL_INVITE_CODE} from "@constants/localkey";
  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 StyledCanvas=styled.canvas`
  38. background-image:url('${spinBg}');
  39. `
  40. const turnObj={
  41. timer:null,
  42. cdTimer:null,
  43. count:0,
  44. result:null,
  45. resTimer:null,
  46. flashTimer:null,
  47. resetTimer:null,
  48. ishelp:false
  49. };
  50. const WWheel=({openShare,showShare=true,onClose=null})=>{
  51. let turnplate=useSelector(getTurnplate);
  52. if(turnplate==null)turnplate={times:0};
  53. const data=[
  54. {id:0,type: 1,value: 0},
  55. {id:1,type: 0,value: 1000},
  56. {id:2,type: 0,value: 50},
  57. {id:3,type: 5,value: 0},
  58. {id:4,type: 1,value: 0},
  59. {id:5,type: 4,value: 0},
  60. {id:6,type: 2,value: 0},
  61. {id:7,type: 0,value: 1},
  62. ]
  63. const drawRef=useRef(null);
  64. const [imgLoaded,setImgLoaded]=useState(false);
  65. const dataLen=8;
  66. const startRotateDegree=3240;
  67. const startRotateDegreeDynamic=3240;
  68. const [drawStyle,setDrawStyle]=useState({});
  69. const [countdown,setCountdown]=useState(turnplate?turnplate.countdown:0);
  70. const isLogin=useSelector(isLogined);
  71. const navigate=useNavigate();
  72. const dispatch = useDispatch();
  73. const stopduring=5.5;
  74. const resetduring=2;
  75. const [collect,setCollect]=useState(0);
  76. const [isLock,setIsLock]=useState(false);
  77. const [openPopInfo,setOpenPopInfo]=useState(false);
  78. const isHelpdone=useSelector(getIsHelp);
  79. const loadImgs=()=>{
  80. let totalCount=spinImgSrc.length;
  81. let loadedCount=0;
  82. let checkDone=()=>{
  83. if (loadedCount===totalCount){
  84. setImgLoaded(true);
  85. }
  86. }
  87. for (let i = 0; i < totalCount; i++) {
  88. let imgSrc=spinImgSrc[i];
  89. if (imgSrc==null){
  90. loadedCount++;
  91. spinImg[i]=null;
  92. checkDone();
  93. }
  94. else{
  95. let img=new Image;
  96. img.src=imgSrc;
  97. spinImg[i]=img;
  98. img.onload=()=>{
  99. loadedCount++;
  100. checkDone();
  101. }
  102. }
  103. }
  104. }
  105. const clearCDTimer=()=>{
  106. if (turnObj.cdTimer!=null){
  107. clearInterval(turnObj.cdTimer);
  108. turnObj.cdTimer=null;
  109. }
  110. }
  111. const checkCountdown=()=>{
  112. if (countdown>0){
  113. clearCDTimer();
  114. let left=countdown;
  115. turnObj.cdTimer=setInterval(()=>{
  116. left--;
  117. if (left<=0){
  118. clearCDTimer();
  119. setCountdown(0);
  120. return;
  121. }
  122. setCountdown(left);
  123. },1000);
  124. }
  125. }
  126. const onTurnResult=(data,code=200)=>{
  127. if (code!==200){
  128. resetWheel(0);
  129. return;
  130. }
  131. let type=data.type;
  132. let curr_amount=turnplate.collect_amount;
  133. let collect_amount=data.collect_amount;
  134. let change=(collect_amount-curr_amount);
  135. let id=-1;
  136. if (type===1){
  137. id=5;
  138. }
  139. else if (type===2){
  140. let ids=[0,4];
  141. let index=Math.floor(Math.random()*ids.length);
  142. id=ids[index];
  143. }
  144. turnObj.result={
  145. type:type,
  146. reward:id,
  147. amount:collect_amount,
  148. change:change
  149. };
  150. // turnObj.ishelp=ishelp;
  151. }
  152. const startSpin=()=>{
  153. // if (!isLogin){
  154. // if (onClose)onClose();
  155. // navigate(ROUTE_PATH.SIGN_IN);
  156. // return;
  157. // }
  158. if (isLock)return;
  159. if (turnplate.times<=0)return;
  160. startTurn();
  161. dispatch(reqTurnplateKan(onTurnResult));
  162. dispatch(updateTimes({times:(turnplate.times-1)}))
  163. setIsLock(true);
  164. }
  165. const clearTurnTimer=()=>{
  166. if (turnObj.timer!=null){
  167. clearInterval(turnObj.timer);
  168. turnObj.timer=null;
  169. }
  170. }
  171. const startTurn=()=>{
  172. clearTurnTimer();
  173. turnObj.count=0;
  174. let turnCount=turnObj.count;
  175. let during=0.333;
  176. let roteStyle={
  177. transform:`rotate(360deg)`,
  178. transition:`transform ${during*2}s cubic-bezier(0.42, 0.00, 1.00, 1.00)`,
  179. }
  180. setDrawStyle(roteStyle);
  181. let timer=setInterval(()=>{
  182. turnCount++;
  183. turnObj.count++;
  184. roteStyle={
  185. transform:`rotate(${turnCount*360}deg)`,
  186. transition:`transform ${during}s cubic-bezier(1.00, 1.00, 0.00, 0.00)`,
  187. }
  188. setDrawStyle(roteStyle);
  189. if(turnCount>2){
  190. if (turnObj.result){
  191. stopTurn(turnObj.result.reward);
  192. }
  193. }
  194. },during*1000);
  195. turnObj.timer=timer;
  196. }
  197. const stopTurn=(reward)=>{
  198. let index=0;
  199. for (let i = 0; i < dataLen; i++) {
  200. let item=data[i];
  201. if (item&&item.id===reward){
  202. index=i;
  203. break;
  204. }
  205. }
  206. let per=360/dataLen;
  207. let angle=turnObj.count*360+startRotateDegreeDynamic-(per*index);
  208. drawAnimation(angle);
  209. }
  210. const drawAnimation=(angle=startRotateDegreeDynamic)=>{
  211. clearTurnTimer();
  212. let roteStyle={
  213. transform:"rotate(".concat(angle, "deg)"),
  214. transition:`transform ${stopduring}s cubic-bezier(0.250, 0.460, 0.455, 0.995)`,
  215. };
  216. setDrawStyle(roteStyle);
  217. playRes(angle);
  218. }
  219. const flashDraw=()=>{
  220. let show=false;
  221. turnObj.flashTimer=setInterval(()=>{
  222. show=!show;
  223. let result=show?turnObj.result:null;
  224. drawWheel(result)
  225. },260)
  226. }
  227. const playRes=(angle=0)=>{
  228. clearResTimer();
  229. let during=(stopduring+0.5)*1000;
  230. turnObj.resTimer=setTimeout(()=>{
  231. flashDraw();
  232. if (turnObj.result.type===2){
  233. setCollect(turnObj.result.change);
  234. }
  235. else if (turnObj.result.type===1){
  236. }
  237. if(!turnObj.ishelp)dispatch(updateCollect({collect_amount:turnObj.result.amount}))
  238. startReset(angle);
  239. },during)
  240. }
  241. // const
  242. const clearResTimer=()=>{
  243. if (turnObj.resTimer){
  244. clearTimeout(turnObj.resTimer);
  245. turnObj.resTimer=null;
  246. }
  247. if (turnObj.flashTimer){
  248. clearInterval(turnObj.flashTimer);
  249. turnObj.flashTimer=null;
  250. }
  251. }
  252. const startReset=(angle=0)=>{
  253. turnObj.resetTimer=setTimeout(()=>{
  254. checkShowPopInfo();
  255. resetWheel(angle);
  256. },resetduring*1000)
  257. }
  258. const checkShowPopInfo=()=>{
  259. let type=2;
  260. if (turnObj.result){
  261. type=turnObj.result.type;
  262. }
  263. if(type===2)setOpenPopInfo(true);
  264. }
  265. const resetWheel=(angle=0)=>{
  266. clearTurnTimer();
  267. turnObj.result=null;
  268. turnObj.count=0;
  269. turnObj.ishelp=false;
  270. if (angle==null)angle=0;
  271. else angle=angle%360;
  272. let roteStyle={
  273. transform:"rotate("+angle+"deg)",
  274. transition:``,
  275. };
  276. setDrawStyle(roteStyle);
  277. drawWheel(null);
  278. setCollect(0);
  279. setIsLock(false);
  280. clearResTimer();
  281. }
  282. const clearResetTimer=()=>{
  283. if (turnObj.resetTimer){
  284. clearTimeout(turnObj.resetTimer);
  285. turnObj.resetTimer=null;
  286. }
  287. }
  288. const drawWheel=(result)=>{
  289. let draw=drawRef.current;
  290. if (draw==null)return;
  291. let colors=["#B90615","#FFD760"];
  292. let color_len=colors.length;
  293. let fontSizeNum=25;
  294. if (data!=null){
  295. let s=draw;
  296. let rdat=data.slice(0,dataLen);
  297. let c=draw.getContext("2d");
  298. let dcwidth=draw.clientWidth;
  299. let dcheight=draw.clientHeight;
  300. let devicePixelRatioNum = window.devicePixelRatio < 1 ? 1 : window.devicePixelRatio;
  301. s.width=dcwidth*devicePixelRatioNum;
  302. s.height=dcheight*devicePixelRatioNum;
  303. c.clearRect(0, 0, s.width, s.height);
  304. let l = 2 * Math.PI / dataLen;
  305. let g = -s.width / 2 + s.width / 2 / 2.5;
  306. c.translate(s.width / 2, s.width / 2);
  307. c.rotate(-2*l);
  308. c.save();
  309. for (let i = 0; i < dataLen; i++) {
  310. let item=rdat[i];
  311. if (item==null)continue;
  312. let color=colors[i%color_len];
  313. if (result){
  314. if(result.reward===item.id){
  315. c.beginPath();
  316. c.moveTo(0, 0);
  317. c.arc(0, 0, s.width / 2 - 6, -l / 2, l / 2.08);
  318. c.strokeStyle = "#fff";
  319. c.fillStyle = "#fff";
  320. c.lineWidth = 10;
  321. c.lineTo(0, -5);
  322. c.stroke();
  323. c.closePath();
  324. }
  325. }
  326. c.rotate(l);
  327. c.save();
  328. c.beginPath();
  329. c.fillStyle = color;
  330. c.textBaseline = "middle";
  331. c.textAlign = "center";
  332. c.font = "".concat(devicePixelRatioNum * fontSizeNum, "px SWISSC-BT");
  333. c.translate(s.width / 5.2, -s.width / dataLen);
  334. c.rotate(.8);
  335. c.translate(-s.width / 5.2, s.width / dataLen);
  336. if (item.type===0){
  337. c.fillText(item.value,s.width / 7.2, g + devicePixelRatioNum * fontSizeNum * .36, 3 * s.width / dataLen);
  338. }
  339. else{
  340. let img=spinImg[item.type];
  341. if(img)c.drawImage(img, s.width / 23, g - 11 * devicePixelRatioNum, s.width / 5, s.width / 4.37);
  342. }
  343. c.restore();
  344. }
  345. }
  346. }
  347. const onHelpResult=(data)=>{
  348. console.log(data);
  349. }
  350. const checkHelpOther=()=>{
  351. let act=localStorageGet(LOCAL_WHEEL_INVITE_CODE);
  352. if (act!=null&&act!==""){
  353. if (!isHelpdone){
  354. dispatch(reqTurnplateHelp(onHelpResult));
  355. }
  356. }
  357. }
  358. useEffect(()=>{
  359. loadImgs();
  360. checkHelpOther();
  361. return ()=>{
  362. clearTurnTimer();
  363. clearCDTimer();
  364. clearResTimer();
  365. turnObj.result=null;
  366. clearResetTimer();
  367. }
  368. },[]);
  369. useEffect(()=>{
  370. if (imgLoaded){
  371. drawWheel();
  372. }
  373. },[imgLoaded])
  374. useEffect(()=>{
  375. checkCountdown();
  376. },[turnplate.countdown])
  377. const renderSpinInfo=()=>{
  378. let times=turnplate?turnplate.times:0;
  379. return (
  380. <>
  381. <div className={styles.spin_con}>
  382. <button className={styles.spin_btn} onClick={()=>{
  383. startSpin();
  384. }}>
  385. <img src={draw} className={styles.spin_img}/>
  386. <div className={styles.spin_content}>
  387. {
  388. collect>0?(
  389. <CountUp
  390. start={0}
  391. end={Number(formatCash(collect))}
  392. duration={1}
  393. separator=" "
  394. decimals={2}
  395. prefix={"+"}
  396. >
  397. {({ countUpRef, start }) => {
  398. return (
  399. <div ref={(ref)=>{
  400. countUpRef.current=ref;
  401. if (countUpRef.collect!==collect){
  402. countUpRef.collect=collect;
  403. start();
  404. }
  405. }} className={styles.collect_num}/>
  406. )
  407. }}
  408. </CountUp>
  409. ):(
  410. <div className={styles.spin_num}>{times}</div>
  411. )
  412. }
  413. </div>
  414. </button>
  415. </div>
  416. </>
  417. )
  418. }
  419. const renderCountDown=()=>{
  420. let d=Math.floor(countdown/86400);
  421. let time=countdown-d*86400;
  422. let stime=formatTime(time);
  423. let sd=i18n.language==="ru"?'д':'d';
  424. return (
  425. <div className={styles.cd_con}>
  426. <div>{i18n.t("turnplate.expires_in")}</div>
  427. <div className={styles.text}>{d+sd}</div>
  428. <div className={styles.text}>{stime}</div>
  429. </div>
  430. )
  431. }
  432. const renderShareBar=()=>{
  433. if (showShare){
  434. return (
  435. <button className={styles.share_con} onClick={()=>{
  436. if (openShare)openShare();
  437. }}>
  438. <div className={styles.share_bar}>
  439. <div className={styles.text}>{i18n.t("turnplate.invite_tips")}</div>
  440. <ShareIcon className={styles.icon}/>
  441. </div>
  442. </button>
  443. )
  444. }
  445. else {
  446. return (<></>);
  447. }
  448. }
  449. const renderPopInfo=()=>{
  450. return (
  451. <WPopInfo open={openPopInfo} onClose={()=>{
  452. setOpenPopInfo(false);
  453. }} openShare={openShare}></WPopInfo>
  454. )
  455. }
  456. return (
  457. <div className={styles.container}>
  458. <div className={styles.wheel}>
  459. <img src={bg_light} className={styles.bg_light}/>
  460. <img src={d_bg} className={styles.lamp}/>
  461. <div className={styles.drawContent}>
  462. <StyledCanvas ref={drawRef} className={styles.draw} style={drawStyle}/>
  463. </div>
  464. <img src={light1} className={`${styles.light} ${styles.light_ani_1}`}/>
  465. <img src={light2} className={`${styles.light} ${styles.light_ani_2}`}/>
  466. <img src={pointer} className={styles.pointer}/>
  467. {
  468. renderSpinInfo()
  469. }
  470. </div>
  471. {renderCountDown()}
  472. {renderShareBar()}
  473. {renderPopInfo()}
  474. </div>
  475. )
  476. }
  477. export default WWheel;