index.jsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. import styles from "@components/GameList/styles.module.scss";
  2. import React, { useCallback, useEffect, useRef, useState } from "react";
  3. import GameItem, { GameImgAspectRatio } from "@components/GameItem";
  4. import { REQ_STATUS } from "@api/config";
  5. import SmallLoading from "@components/SmallLoading";
  6. import { useThemeProvider } from "@contexts/themeContext";
  7. import GameRoom from "@components/GameRoom";
  8. import { useWindowSize } from "react-use";
  9. const i18n = global.i18n;
  10. const objInfo = {
  11. timer: null
  12. }
  13. const GameList = ({ data, status, reqMore, user }) => {
  14. // data={
  15. // type:0,//游戏分类
  16. // page:1,
  17. // pageSize:40,
  18. // total:42,
  19. // totalPage:1,
  20. // data:[
  21. // {
  22. // gid:0,//gameid
  23. // img:"https://bc.imgix.net/game/image/f76d3f4a-9712-4bd1-9426-f3ab7069b6a2.png?auto=format&dpr=3&w=200",//图片
  24. // link:"",//跳转的链接
  25. // online:1006,//游戏人数
  26. // desc:"BC Originals",//描述
  27. // },
  28. // {
  29. // gid:0,//gameid
  30. // img:"https://bc.imgix.net/game/image/ff0b0511-1f48-43d1-8dd3-294bb6437b4b.png?auto=format&dpr=3&w=200",//图片
  31. // link:"",//跳转的链接
  32. // online:100,//游戏人数
  33. // desc:"BC Originals",//描述
  34. // },
  35. // {
  36. // gid:0,//gameid
  37. // img:"https://bc.imgix.net/game/image/d33b3fad-b6df-450b-bd85-528a17383043.png?auto=format&dpr=3&w=200",//图片
  38. // link:"",//跳转的链接
  39. // online:100,//游戏人数
  40. // desc:"BC Originals",//描述
  41. // },
  42. // {
  43. // gid:0,//gameid
  44. // img:"https://bc.imgix.net/game/image/5b3a4099-9462-4d3c-a854-ade78aad0d60.png?auto=format&dpr=3&w=200",//图片
  45. // link:"",//跳转的链接
  46. // online:100,//游戏人数
  47. // desc:"BC Originals",//描述
  48. // },
  49. // {
  50. // gid:0,//gameid
  51. // img:"https://bc.imgix.net/game/image/f76d3f4a-9712-4bd1-9426-f3ab7069b6a2.png?auto=format&dpr=3&w=200",//图片
  52. // link:"",//跳转的链接
  53. // online:100,//游戏人数
  54. // desc:"BC Originals",//描述
  55. // },
  56. // {
  57. // gid:0,//gameid
  58. // img:"https://bc.imgix.net/game/image/ff0b0511-1f48-43d1-8dd3-294bb6437b4b.png?auto=format&dpr=3&w=200",//图片
  59. // link:"",//跳转的链接
  60. // online:100,//游戏人数
  61. // desc:"BC Originals",//描述
  62. // },
  63. // {
  64. // gid:0,//gameid
  65. // img:"https://bc.imgix.net/game/image/d33b3fad-b6df-450b-bd85-528a17383043.png?auto=format&dpr=3&w=200",//图片
  66. // link:"",//跳转的链接
  67. // online:100,//游戏人数
  68. // desc:"BC Originals",//描述
  69. // },
  70. // {
  71. // gid:0,//gameid
  72. // img:"https://bc.imgix.net/game/image/5b3a4099-9462-4d3c-a854-ade78aad0d60.png?auto=format&dpr=3&w=200",//图片
  73. // link:"",//跳转的链接
  74. // online:100,//游戏人数
  75. // desc:"BC Originals",//描述
  76. // },
  77. // ]
  78. // }
  79. const { isMobile, fontScale } = useThemeProvider();
  80. const M_LIST_COL = 3;
  81. const [selected, setSelected] = useState(-1);
  82. const [roomRow, setRoomRow] = useState({});
  83. const [showPopRoom, setShowPopRoom] = useState(false);
  84. const roomHeight = 5.2;
  85. const rowTop = roomHeight + 0.75;
  86. const gameListRef = useRef(null);
  87. const [gridInfo, setGridInfo] = useState({});
  88. const { width } = useWindowSize();
  89. const mobile = isMobile();
  90. const handleReset = (e) => {
  91. setSelected(-1);
  92. setRoomRow({});
  93. }
  94. useEffect(() => {
  95. document.addEventListener("click", handleReset);
  96. return () => {
  97. document.removeEventListener("click", handleReset);
  98. resetTimer();
  99. }
  100. }, [])
  101. const getDesktopCol = useCallback(() => {
  102. if (width >= 1264 && width <= 1564) return 6;
  103. else if (width >= 963 && width <= 1263) return 5;
  104. else if (width >= 622 && width <= 962) return 4;
  105. else if (width <= 621) return 3;
  106. return 7;
  107. }, [width]);
  108. const initGridsInfo = useCallback(() => {
  109. let parent = gameListRef.current;
  110. let totalCol = 3;
  111. if (!mobile) totalCol = getDesktopCol();
  112. let gridBaseRatio = GameImgAspectRatio;
  113. let totalWidth = mobile ? 100 * totalCol : 160 * totalCol;
  114. if (parent != null) totalWidth = parent.clientWidth;
  115. let rowGap = parent != null ? window.getComputedStyle(parent).getPropertyValue('grid-row-gap') : "0px";
  116. let gap = Number(rowGap.replace("px", ""));
  117. if (gap === 0 || isNaN(gap)) gap = 0.75 * 16 * fontScale;
  118. let gridsData = {};
  119. for (let i = 1; i <= 6; i++) {
  120. let gridNum = i;
  121. let gridWidth = (totalWidth - (totalCol - 1) * gap) / totalCol;
  122. let itemWidth = gridWidth * gridNum + (gridNum - 1) * gap;
  123. let width = itemWidth + "px";
  124. let aspectRatio = gridBaseRatio * (itemWidth / gridWidth);
  125. gridsData[i] = { width, aspectRatio };
  126. }
  127. setGridInfo(gridsData);
  128. }, [fontScale, getDesktopCol, mobile]);
  129. useEffect(() => {
  130. initGridsInfo();
  131. }, [initGridsInfo]);
  132. const resetTimer = () => {
  133. if (objInfo.timer != null) {
  134. clearTimeout(objInfo.timer);
  135. objInfo.timer = null;
  136. }
  137. }
  138. const checkSetRoom = (e, item, row, total) => {
  139. let target = e.target;
  140. if (target) {
  141. let rect = target.getBoundingClientRect();
  142. let cH = document.documentElement.clientHeight;
  143. let cB = rect.bottom;
  144. if (cH - cB > 90) row = row + 1;
  145. } else row = row + 1;
  146. if (row >= total) row = total - 1;
  147. let roomRowItem = {
  148. row: row,
  149. data: item
  150. }
  151. if (roomRow.row === row) {
  152. setRoomRow({
  153. hide: true,
  154. ...roomRowItem
  155. });
  156. resetTimer();
  157. objInfo.timer = setTimeout(() => {
  158. setRoomRow(roomRowItem);
  159. }, 10)
  160. }
  161. else setRoomRow(roomRowItem);
  162. }
  163. const handleSelect = (e, data, row, total) => {
  164. e.stopPropagation();
  165. if (data.id === selected) {
  166. handleReset();
  167. }
  168. else {
  169. setSelected(data.id);
  170. checkSetRoom(e, data, row, total);
  171. }
  172. }
  173. const handlePopSelected = (e, data, row, totall) => {
  174. e.stopPropagation();
  175. setRoomRow({
  176. row: -1,
  177. data: data
  178. });
  179. setShowPopRoom(true);
  180. }
  181. const handlePopClose = () => {
  182. setRoomRow({});
  183. setShowPopRoom(false);
  184. }
  185. const renderGameListDesktop = () => {
  186. let list = data.data;
  187. if (list == null) list = [];
  188. return (
  189. <>
  190. <div ref={gameListRef} id={"gameList"} className={styles.game_list}>
  191. {
  192. list.map((item, index) => {
  193. let gridNum = item.gridNum != null ? item.gridNum : 1;
  194. // gridNum=Math.floor(Math.random()*3)+1;
  195. let whStyle = gridInfo[gridNum];
  196. if (whStyle == null) return null;
  197. return (
  198. <GameItem key={index} data={item} handleRoom={handlePopSelected} whStyle={whStyle}></GameItem>
  199. )
  200. })
  201. }
  202. </div>
  203. <GameRoom open={showPopRoom} data={roomRow.data} user={user} onClose={handlePopClose}></GameRoom>
  204. </>
  205. )
  206. }
  207. const formatMList = (list) => {
  208. let mList = [];
  209. if (list) {
  210. let count = 0;
  211. for (let i = 0; i < list.length; i++) {
  212. let item = list[i];
  213. let gridNum = item.gridNum != null ? item.gridNum : 1;
  214. let row = Math.floor(count / M_LIST_COL);
  215. let rowData = mList[row];
  216. if (rowData == null) rowData = [];
  217. let nitem = { ...item, gridNum: gridNum }
  218. rowData.push(nitem);
  219. mList[row] = rowData;
  220. count += gridNum;
  221. }
  222. }
  223. return mList;
  224. }
  225. const renderRoom = (row, len) => {
  226. if (roomRow == null) return (<></>);
  227. if (roomRow.hide) return (<></>);
  228. let rrow = roomRow.row;
  229. let data = roomRow.data;
  230. if (rrow === row) {
  231. return (
  232. <GameRoom open={true} data={data} height={roomHeight + "rem"} mobile={true} user={user}></GameRoom>
  233. )
  234. }
  235. else return (<></>);
  236. }
  237. const renderGameListMobile = () => {
  238. let list = data.data;
  239. if (list == null) list = [];
  240. list = formatMList(list);
  241. let len = list.length;
  242. return (
  243. <div ref={gameListRef} id={"gameList"} className={styles.game_list_mobile}>
  244. {
  245. list.map((rowArr, row) => {
  246. let rowStyle = {};
  247. if (roomRow != null) {
  248. if (row >= roomRow.row) {
  249. rowStyle.transform = `translateY(${rowTop + "rem"})`;
  250. }
  251. }
  252. return (
  253. <React.Fragment key={row}>
  254. {renderRoom(row, len)}
  255. <div key={row} className={styles.list_row} style={rowStyle}>
  256. {
  257. rowArr.map((item, col) => {
  258. let itemStyle = {}
  259. if (selected !== -1) {
  260. if (item.id !== selected) {
  261. itemStyle.filter = "brightness(60%)"
  262. }
  263. else {
  264. itemStyle.transform = "scale(1.05)";
  265. }
  266. }
  267. let gridNum = item.gridNum != null ? item.gridNum : 1;
  268. let whStyle = gridInfo[gridNum];
  269. return (
  270. <GameItem key={`${row}-${col}`} data={item} handleRoom={(e, data) => {
  271. handleSelect(e, data, row, len)
  272. }} row={row} whStyle={whStyle} style={itemStyle}></GameItem>
  273. )
  274. })
  275. }
  276. </div>
  277. </React.Fragment>
  278. )
  279. })
  280. }
  281. </div>
  282. )
  283. }
  284. const renderGameList = () => {
  285. if (isMobile()) return renderGameListMobile();
  286. else return renderGameListDesktop();
  287. }
  288. const renderProgress = () => {
  289. let list = data.data;
  290. if (list == null) list = [];
  291. let current = list.length;
  292. let total = data.total;
  293. let progress = Math.ceil((current / total) * 100);
  294. let spro = progress + "%";
  295. return (
  296. <div className={styles.page_progress}>
  297. <span>{current} / {total}</span>
  298. <div className={styles.progress}>
  299. <div className={styles.green} style={{ width: spro }}></div>
  300. </div>
  301. <span>{spro}</span>
  302. </div>
  303. )
  304. }
  305. const renderLoading = () => {
  306. return (<SmallLoading fill={"#fff"} />)
  307. }
  308. const onClickMore = () => {
  309. if (reqMore) {
  310. let page = Number(data.page) + 1;
  311. let tabtype = data.tabtype;
  312. let id = data.id;
  313. reqMore(id, tabtype, page);
  314. }
  315. }
  316. const renderShowMoreBtn = () => {
  317. let isNomore = Number(data.page) === Number(data.totalPage);
  318. let label = isNomore ? i18n.t("game_list.no_more") : i18n.t("game_list.show_more");
  319. return (
  320. <div className={styles.show_more}>
  321. <button className={styles.more_btn} disabled={isNomore} onClick={() => {
  322. if (status !== REQ_STATUS.LOADING) {
  323. onClickMore();
  324. }
  325. }}>
  326. {
  327. status === REQ_STATUS.LOADING ? renderLoading() : (<div>{label}</div>)
  328. }
  329. </button>
  330. </div>
  331. )
  332. }
  333. const renderShowMore = () => {
  334. let isNomore = Number(data.page) === Number(data.totalPage);
  335. let moreStyle = {};
  336. if (roomRow && roomRow.row != null) {
  337. let row = roomRow.row;
  338. if (row >= 0) {
  339. moreStyle.transform = `translateY(${rowTop + "rem"})`;
  340. moreStyle.marginBottom = rowTop + "rem";
  341. }
  342. }
  343. if (isNomore) {
  344. if (isMobile()) return (<div className={`${styles.page_con} ${styles.page_nomore}`} style={moreStyle}></div>)
  345. else return (<></>);
  346. }
  347. return (
  348. <div className={styles.page_con} style={moreStyle}>
  349. {renderProgress()}
  350. {renderShowMoreBtn()}
  351. </div>
  352. )
  353. }
  354. return (
  355. <div className={`w-100 ${styles.list_container}`}>
  356. {renderGameList()}
  357. {renderShowMore()}
  358. </div>
  359. )
  360. }
  361. export default GameList;