You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2108 lines
46 KiB

1 month ago
4 weeks ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <div class="homepage">
  3. <img src="../../../assets/qilin.webp" alt="麒麟" class="hllogo" />
  4. <div class="music" @click="musicPlay()">
  5. <audio id="music" :src="musicFile" class="music-item" loop></audio>
  6. <div id="musicBox" class="music-box" title="播放/暂停背景音乐">Music</div>
  7. </div>
  8. <div ref="qipaoTextRef" class="qipao">{{ qipaoText }}</div>
  9. <div class="leftBar">
  10. <el-scrollbar id="prizeBar">
  11. <ul class="prize-list">
  12. <li
  13. v-for="item in prizes"
  14. :key="item.type"
  15. :id="`prize-item-${item.type}`"
  16. :class="['prize-item']"
  17. @click="lookPrize(item)"
  18. >
  19. <div
  20. v-if="item.isLook"
  21. style="display: flex; width: 100%; height: 100%"
  22. >
  23. <span></span><span></span><span></span><span></span>
  24. <div class="prize-img">
  25. <img :src="item.img" :alt="item.title" />
  26. </div>
  27. <div class="prize-text">
  28. <div class="prize-title">
  29. <div class="level">
  30. {{ item.gradeName }}
  31. </div>
  32. {{ item.prizeName }}
  33. </div>
  34. <div class="prize-count">
  35. <div class="progress">
  36. <div
  37. :id="`prize-bar-${item.type}`"
  38. class="progress-bar progress-bar-danger progress-bar-striped active"
  39. style="width: 100%"
  40. ></div>
  41. </div>
  42. <div
  43. :id="`prize-count-${item.type}`"
  44. class="prize-count-left"
  45. >
  46. {{ item.leftCount }}/{{ item.count }}
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. <div v-else style="display: flex; width: 100%; height: 100%">
  52. <img
  53. src="../../../assets/img/待揭秘.png"
  54. alt="待揭秘"
  55. class="readyLook"
  56. />
  57. </div>
  58. </li>
  59. </ul>
  60. </el-scrollbar>
  61. <div v-if="!isOpen">
  62. <div class="getPrizeName" @click="openGetPrize()">
  63. <img src="../../../assets/img/展开.png" alt="展开" class="open" />
  64. 获奖名单
  65. </div>
  66. </div>
  67. <div v-else>
  68. <div class="dgetPrizeName">
  69. <img
  70. src="../../../assets/img/展开.png"
  71. alt="展开"
  72. class="close"
  73. @click="closeGetPrize()"
  74. />
  75. <div class="tableHead">
  76. <div class="tableHead1">HomilyID</div>
  77. <div class="tableHead2">奖项</div>
  78. <div class="gradient-line"></div>
  79. </div>
  80. <el-scrollbar class="tableBody">
  81. <div v-for="item in getPrizeUserList" :key="item">
  82. <div class="tableItem">
  83. <div class="tableItem1">
  84. {{ item.jwcode }}
  85. </div>
  86. <div class="tableItem2">
  87. {{ item.gradeName }}
  88. </div>
  89. </div>
  90. </div>
  91. </el-scrollbar>
  92. </div>
  93. </div>
  94. </div>
  95. <div id="container">
  96. <div ref="threeContainer" class="three-container"></div>
  97. <!-- 分页指示器 -->
  98. <div v-if="pageMaxIndex > 1" class="page-indicator">
  99. {{ pageIndex + 1 }} / {{ pageMaxIndex }}
  100. </div>
  101. </div>
  102. <!-- <img
  103. src="../../../assets/img/展开.png"
  104. alt="向右"
  105. @click="rightPage()"
  106. class="rightPage"
  107. id="rightPage"
  108. />
  109. <img
  110. src="../../../assets/img/展开.png"
  111. alt="向左"
  112. @click="leftPage()"
  113. class="leftPage"
  114. id="leftPage"
  115. /> -->
  116. <div id="menu">
  117. <div id="enter" ref="enterRef" @click="enterLottery()" class="btn">
  118. 进入抽奖
  119. </div>
  120. <div id="lotteryBar" ref="lotteryBarRef" class="none">
  121. <div id="lottery" ref="lotteryRef" @click="lotteryBtn()" class="btn">
  122. 开始抽奖
  123. </div>
  124. </div>
  125. </div>
  126. </div>
  127. </template>
  128. <script setup>
  129. import { ref, onMounted, reactive, nextTick } from "vue";
  130. import { useRouter } from "vue-router";
  131. import musicFile from "../../../data/music.mp3";
  132. import * as THREE from "three";
  133. import axios from "axios";
  134. import "../../../assets/css/animate.min.css";
  135. import { resetPrize } from "../../../utils/prizeList";
  136. import { NUMBER_MATRIX } from "../../../utils/config";
  137. import { CSS3DObject, CSS3DRenderer } from "../../../utils/CSS3DRenderer.js";
  138. import { TrackballControls } from "../../../utils/TrackballControls.js";
  139. import {
  140. getPrizeListApi,
  141. getUserListApi,
  142. getGetPrizeUserListApi,
  143. startLotteryApi,
  144. } from "../../../api/API";
  145. // 常量
  146. const ROTATE_TIME = 10000; //旋转动画的总时长
  147. const ROTATE_LOOP = 1000;
  148. const BASE_HEIGHT = 1080; //高度
  149. // 按钮控制
  150. const enterRef = ref(null); //进入抽奖按钮
  151. const lotteryBarRef = ref(null); //开始抽奖元素组(显示控制)
  152. const lotteryRef = ref(null); //开始抽奖按钮(文字控制 )
  153. //3D相关变量
  154. // 当前的比例
  155. let Resolution = 0.9;
  156. //总卡片数
  157. let TOTAL_CARDS;
  158. // 高度卡片数
  159. let ROW_COUNT = 7;
  160. // 宽度卡片数
  161. let COLUMN_COUNT = 21;
  162. // 高亮卡片数组
  163. let HIGHLIGHT_CELL = [];
  164. // 其他变量
  165. let camera,
  166. scene,
  167. renderer,
  168. controls,
  169. threeDCards = [],
  170. targets = {
  171. table: [],
  172. sphere: [],
  173. };
  174. // 动画对象
  175. let rotateObj;
  176. // 控制旋转
  177. let rotate = false;
  178. //奖品列表
  179. const prizes = ref([]);
  180. //每个奖品每次抽取的数目
  181. let EACH_COUNT = [];
  182. const users = ref([]);
  183. const getPrizeUsers = ref([]);
  184. //判断是否正在抽奖
  185. let isLotting = false;
  186. const setLotteryStatus = (status = false) => {
  187. isLotting = status;
  188. };
  189. let selectedCardIndex = [];
  190. let interval;
  191. // 当前抽的奖项,从最低奖开始抽,直到抽到大奖
  192. let currentPrizeIndex;
  193. const currentPrize = ref({});
  194. let currentLuckys = [];
  195. let prizeElement = {};
  196. const qipaoText = ref("");
  197. const qipaoTextRef = ref(null);
  198. const addQipao = (text) => {
  199. qipaoTextRef.value.style.display = "block";
  200. qipaoTextRef.value.style.opacity = "1";
  201. qipaoText.value = text;
  202. setTimeout(() => {
  203. // 设置过渡效果
  204. qipaoTextRef.value.style.transition = "opacity 0.5s ease-out";
  205. // 开始渐隐
  206. qipaoTextRef.value.style.opacity = "0";
  207. // 动画完成后隐藏元素
  208. setTimeout(() => {
  209. qipaoTextRef.value.style.display = "none";
  210. }, 800);
  211. }, 1000);
  212. };
  213. const getPrizeUserList = ref([]);
  214. const isOpen = ref(false);
  215. const openGetPrize = async () => {
  216. if (!prizes.value[prizes.value.length - 1].isLook) {
  217. addQipao("请先揭晓奖品。");
  218. return;
  219. }
  220. let res = await getGetPrizeUserListApi({});
  221. getPrizeUserList.value = res.data.list;
  222. isOpen.value = true;
  223. // console.log("currentPrize", currentPrize.value);
  224. prizes.value.forEach((item) => {
  225. // console.log("item", item);
  226. if (item.type != currentPrize.value.type) {
  227. let box = document.querySelector(`#prize-item-${item.type}`);
  228. box.style.display = "none";
  229. }
  230. });
  231. let scroll = document.getElementById("prizeBar");
  232. // scroll.style.height = "110px";
  233. };
  234. const closeGetPrize = () => {
  235. isOpen.value = false;
  236. prizes.value.forEach((item) => {
  237. // console.log("item", item);
  238. if (item.type != currentPrize.value.type) {
  239. let box = document.querySelector(`#prize-item-${item.type}`);
  240. box.style.display = "flex";
  241. }
  242. });
  243. // let scroll = document.getElementById("prizeBar");
  244. // scroll.style.height = "650px";
  245. };
  246. //揭示奖品
  247. const lookPrize = (item) => {
  248. // 未进入抽奖禁止揭晓
  249. if (!joinLottery) {
  250. addQipao("请先进入抽奖。");
  251. return;
  252. }
  253. // 如果已经揭示,那返回
  254. if (item.isLook) return;
  255. //最低级的可以直接揭晓
  256. if (
  257. currentPrize.value.type == prizes.value[prizes.value.length - 1].type &&
  258. item.type == currentPrize.value.type
  259. ) {
  260. let currentBox = document.querySelector(`#prize-item-${item.type}`);
  261. currentBox && currentBox.classList.add("shine");
  262. //点击揭晓
  263. item.isLook = true;
  264. } else if (
  265. !isLotting && //未在抽奖状态
  266. currentPrize.value.leftCount == 0 && //当前奖项已抽完
  267. prizes.value[currentPrizeIndex - 1] == item //点击的奖项是当前奖项的下一个
  268. ) {
  269. currentPrize.value = item;
  270. currentPrizeIndex--;
  271. let lastIndex = -1;
  272. for (let i = prizes.value.length - 1; i >= 0; i--) {
  273. if (prizes.value[i].type == item.type) {
  274. if (i != prizes.value.length - 1) {
  275. lastIndex = i + 1;
  276. }
  277. break;
  278. }
  279. }
  280. if (lastIndex != -1) {
  281. let lastPrize = prizes.value[lastIndex];
  282. // console.log("lastPrize", lastPrize);
  283. let lastBox = document.querySelector(`#prize-item-${lastPrize.type}`);
  284. lastBox.classList.remove("shine");
  285. lastBox.classList.add("done");
  286. let currentBox = document.querySelector(`#prize-item-${item.type}`);
  287. currentBox && currentBox.classList.add("shine");
  288. }
  289. //点击揭晓
  290. item.isLook = true;
  291. }
  292. };
  293. function setPrizeData(currentPrizeIndex, isInit) {
  294. // console.log("prizes", prizes.value);
  295. // let currentPrize = prizes.value[currentPrizeIndex];
  296. let type = currentPrize.value.type;
  297. let elements = prizeElement[type];
  298. let count = currentPrize.value.leftCount;
  299. let totalCount = currentPrize.value.count;
  300. if (!elements || !elements.bar || !elements.text) {
  301. elements = {
  302. box: document.querySelector(`#prize-item-${type}`),
  303. bar: document.querySelector(`#prize-bar-${type}`),
  304. text: document.querySelector(`#prize-count-${type}`),
  305. };
  306. }
  307. if (isInit) {
  308. for (let i = prizes.value.length - 1; i > currentPrizeIndex; i--) {
  309. let type = prizes.value[i]["type"];
  310. document.querySelector(`#prize-item-${type}`).className =
  311. "prize-item done";
  312. document.querySelector(`#prize-bar-${type}`).style.width = "0";
  313. document.querySelector(`#prize-count-${type}`).textContent =
  314. "0" + "/" + prizes.value[i]["count"];
  315. }
  316. }
  317. // console.log("currentPrize", currentPrize);
  318. let percent = (count / totalCount).toFixed(2);
  319. if (elements.bar) {
  320. elements.bar.style.width = percent * 100 + "%";
  321. }
  322. }
  323. /**
  324. * 初始化所有DOM
  325. */
  326. const pageIndex = ref(0);
  327. const pageMaxIndex = ref(0);
  328. const cardsPerPage = 10; // 每页显示的卡片数量
  329. let isPageTransitioning = false; // 防止页面切换时的重复操作
  330. // 全局变量存储当前选中的卡片索引数组
  331. let globalCardIndexes = [];
  332. const initAll = async () => {
  333. const [prizeList, userList] = await Promise.all([
  334. getPrizeListApi(),
  335. getUserListApi(),
  336. ]);
  337. // 奖品列表
  338. prizes.value = prizeList.data;
  339. // 用户列表
  340. users.value = userList.data;
  341. prizes.value.forEach((item, index) => {
  342. item.type = index;
  343. item.count = item.amount;
  344. item.leftCount = item.amount; //剩余次数(用于计算奖品下方的进度条的百分比)
  345. item.hasCount = item.amount; //已抽次数(用于计算出奖)
  346. item.isLook = false; //
  347. EACH_COUNT.push(item.perWin);
  348. });
  349. console.log("prizes", prizes.value);
  350. console.log("EACH_COUNT", EACH_COUNT);
  351. HIGHLIGHT_CELL = createHighlight();
  352. TOTAL_CARDS = ROW_COUNT * COLUMN_COUNT;
  353. currentPrizeIndex = prizes.value.length - 1;
  354. currentPrize.value = prizes.value[currentPrizeIndex];
  355. await nextTick();
  356. setPrizeData(currentPrizeIndex, true);
  357. initCards();
  358. // startMaoPao();
  359. animate();
  360. shineCard();
  361. // window.AJAX({
  362. // url: "/getUsers",
  363. // success(data) {},
  364. // });
  365. };
  366. const initCards = () => {
  367. let member = users.value.slice(),
  368. showCards = [],
  369. length = member.length;
  370. let isBold = false;
  371. let index = 0;
  372. let totalMember = member.length;
  373. let position = {
  374. x: (140 * COLUMN_COUNT - 20) / 2,
  375. y: (180 * ROW_COUNT - 20) / 2,
  376. };
  377. camera = new THREE.PerspectiveCamera(
  378. 40,
  379. window.innerWidth / window.innerHeight,
  380. 1,
  381. 10000
  382. );
  383. camera.position.z = 3000;
  384. scene = new THREE.Scene();
  385. for (let i = 0; i < ROW_COUNT; i++) {
  386. for (let j = 0; j < COLUMN_COUNT; j++) {
  387. isBold = HIGHLIGHT_CELL.includes(j + "-" + i);
  388. var element = createCard(member[index % length], isBold, index);
  389. var object = new CSS3DObject(element);
  390. object.position.x = Math.random() * 4000 - 2000;
  391. object.position.y = Math.random() * 4000 - 2000;
  392. object.position.z = Math.random() * 4000 - 2000;
  393. scene.add(object);
  394. threeDCards.push(object);
  395. //
  396. var object = new THREE.Object3D();
  397. object.position.x = j * 155 - position.x;
  398. object.position.y = -(i * 195) + position.y;
  399. targets.table.push(object);
  400. index++;
  401. }
  402. }
  403. // sphere
  404. var vector = new THREE.Vector3();
  405. for (var i = 0, l = threeDCards.length; i < l; i++) {
  406. var phi = Math.acos(-1 + (2 * i) / l);
  407. var theta = Math.sqrt(l * Math.PI) * phi;
  408. var object = new THREE.Object3D();
  409. object.position.setFromSphericalCoords(800 * Resolution, phi, theta);
  410. vector.copy(object.position).multiplyScalar(2);
  411. object.lookAt(vector);
  412. targets.sphere.push(object);
  413. }
  414. renderer = new CSS3DRenderer();
  415. renderer.setSize(window.innerWidth * 1, window.innerHeight * 0.9);
  416. renderer.domElement.style.margin = "7% 0 0 1%";
  417. // document.getElementById("container").appendChild(renderer.domElement);
  418. if (threeContainer.value) {
  419. threeContainer.value.appendChild(renderer.domElement);
  420. }
  421. //
  422. controls = new TrackballControls(camera, renderer.domElement);
  423. controls.rotateSpeed = 0.5;
  424. controls.minDistance = 500;
  425. controls.maxDistance = 6000;
  426. controls.addEventListener("change", render);
  427. switchScreen("enter");
  428. };
  429. // 判断是否进入抽奖
  430. let joinLottery = false;
  431. // 进入抽奖
  432. const enterLottery = () => {
  433. joinLottery = true;
  434. removeHighlight();
  435. // rotate = !rotate;
  436. rotate = true;
  437. switchScreen("lottery");
  438. };
  439. function switchScreen(type) {
  440. switch (type) {
  441. case "enter":
  442. if (enterRef.value) {
  443. enterRef.value.classList.remove("none");
  444. }
  445. if (lotteryBarRef.value) {
  446. lotteryBarRef.value.classList.add("none");
  447. }
  448. transform(targets.table, 2000);
  449. break;
  450. default:
  451. if (enterRef.value) {
  452. enterRef.value.classList.add("none");
  453. }
  454. if (lotteryBarRef.value) {
  455. lotteryBarRef.value.classList.remove("none");
  456. }
  457. transform(targets.sphere, 2000);
  458. break;
  459. }
  460. }
  461. /**
  462. * 创建元素
  463. */
  464. const createElement = (css, text) => {
  465. let dom = document.createElement("div");
  466. dom.className = css || "";
  467. dom.innerHTML = text || "";
  468. return dom;
  469. };
  470. /**
  471. * 创建名牌
  472. */
  473. const createCard = (user, isBold, id) => {
  474. var element = createElement();
  475. element.id = "card-" + id;
  476. element.style.display = "flex";
  477. element.style.alignItems = "center";
  478. element.style.justifyContent = "center";
  479. if (isBold) {
  480. element.className = "element lightitem";
  481. element.classList.add("highlight");
  482. } else {
  483. element.className = "element";
  484. // element.style.backgroundColor =
  485. // "rgba(255,170,22," + (Math.random() * 0.7 + 0.25) + ")";
  486. element.style.backgroundColor = "rgba(255,170,22,1)";
  487. element.style.border = "2px solid rgba(255, 255, 255, 1)";
  488. }
  489. element.appendChild(createElement("name", user.jwcode));
  490. return element;
  491. };
  492. function removeHighlight() {
  493. document.querySelectorAll(".highlight").forEach((node) => {
  494. node.classList.remove("highlight");
  495. });
  496. }
  497. function addHighlight() {
  498. document.querySelectorAll(".lightitem").forEach((node) => {
  499. node.classList.add("highlight");
  500. });
  501. }
  502. /**
  503. * 渲染地球等
  504. */
  505. function transform(targets, duration) {
  506. // TWEEN.removeAll();
  507. for (var i = 0; i < threeDCards.length; i++) {
  508. var object = threeDCards[i];
  509. var target = targets[i];
  510. new TWEEN.Tween(object.position)
  511. .to(
  512. {
  513. x: target.position.x,
  514. y: target.position.y,
  515. z: target.position.z,
  516. },
  517. Math.random() * duration + duration
  518. )
  519. .easing(TWEEN.Easing.Exponential.InOut)
  520. .start();
  521. new TWEEN.Tween(object.rotation)
  522. .to(
  523. {
  524. x: target.rotation.x,
  525. y: target.rotation.y,
  526. z: target.rotation.z,
  527. },
  528. Math.random() * duration + duration
  529. )
  530. .easing(TWEEN.Easing.Exponential.InOut)
  531. .start();
  532. }
  533. new TWEEN.Tween(this)
  534. .to({}, duration * 2)
  535. .onUpdate(render)
  536. .start();
  537. }
  538. function rotateBall() {
  539. return new Promise((resolve, reject) => {
  540. scene.rotation.y = 0;
  541. rotateObj = new TWEEN.Tween(scene.rotation);
  542. rotateObj
  543. .to(
  544. {
  545. y: Math.PI * 6 * ROTATE_LOOP,
  546. },
  547. ROTATE_TIME * ROTATE_LOOP
  548. )
  549. .onUpdate(render)
  550. // .easing(TWEEN.Easing.Linear)
  551. .start()
  552. .onStop(() => {
  553. scene.rotation.y = 0;
  554. resolve();
  555. })
  556. .onComplete(() => {
  557. resolve();
  558. });
  559. });
  560. }
  561. function animate() {
  562. // 让场景通过x轴或者y轴旋转
  563. // rotate && (scene.rotation.y += 0.088);
  564. requestAnimationFrame(animate);
  565. TWEEN.update();
  566. controls.update();
  567. // 渲染循环
  568. // render();
  569. }
  570. function render() {
  571. renderer.render(scene, camera);
  572. }
  573. // 展开
  574. // const rightPage = () => {
  575. // if (pageIndex == pageMaxIndex) return;
  576. // console.log("点击了向右");
  577. // // 先让当前卡片回去,然后显示下一页
  578. // resetCard(300).then(() => {
  579. // pageIndex++;
  580. // selectCard(600, pageIndex);
  581. // });
  582. // };
  583. // const leftPage = () => {
  584. // if (pageIndex == 0) return;
  585. // console.log("点击了向左");
  586. // // 先让当前卡片回去,然后显示上一页
  587. // resetCard(300).then(() => {
  588. // pageIndex--;
  589. // selectCard(600, pageIndex);
  590. // });
  591. // };
  592. //停止抽奖动画
  593. // function selectCard(duration = 600, index = 0) {
  594. // console.log("pageIndex", pageIndex, "pageMaxIndex", pageMaxIndex);
  595. // let right = document.getElementById("rightPage");
  596. // let left = document.getElementById("leftPage");
  597. // right.style.display = "flex";
  598. // left.style.display = "flex";
  599. // pageMaxIndex = Math.ceil(selectedCardIndex.length / 10) - 1;
  600. // let getCurrentIndex = [];
  601. // for (let i = 0; i < selectedCardIndex.length; i += 10) {
  602. // getCurrentIndex.push(selectedCardIndex.slice(i, i + 10));
  603. // }
  604. // let getCurrentLucky = [];
  605. // for (let i = 0; i < currentLuckys.length; i += 10) {
  606. // getCurrentLucky.push(currentLuckys.slice(i, i + 10));
  607. // }
  608. // console.log("getCurrentIndex", getCurrentIndex);
  609. // console.log("getCurrentLucky", getCurrentLucky);
  610. // rotate = false; //停止旋转
  611. // let width = 180;
  612. // let tag = -(getCurrentLucky[index].length - 1) / 2;
  613. // let locates = [];
  614. // // 计算位置信息, 大于5个分两排显示
  615. // if (getCurrentLucky[index].length > 5) {
  616. // let yPosition = [-120, 120];
  617. // let l = getCurrentIndex[index].length;
  618. // let mid = Math.ceil(l / 2);
  619. // tag = -(mid - 1) / 2;
  620. // for (let i = 0; i < mid; i++) {
  621. // locates.push({
  622. // x: tag * width * Resolution,
  623. // y: yPosition[0] * Resolution,
  624. // });
  625. // tag++;
  626. // }
  627. // console.log("locates", locates);
  628. // // console.log("selectedCardIndex", selectedCardIndex);
  629. // // console.log("currentLuckys", currentLuckys);
  630. // tag = -(l - mid - 1) / 2;
  631. // for (let i = mid; i < l; i++) {
  632. // locates.push({
  633. // x: tag * width * Resolution,
  634. // y: yPosition[1] * Resolution,
  635. // });
  636. // tag++;
  637. // }
  638. // } else {
  639. // for (let i = getCurrentIndex[index].length; i > 0; i--) {
  640. // locates.push({
  641. // x: tag * width * Resolution,
  642. // y: 0 * Resolution,
  643. // });
  644. // tag++;
  645. // }
  646. // }
  647. // getCurrentIndex[index].forEach((cardIndex, iindex) => {
  648. // changeCard(cardIndex, getCurrentLucky[index][iindex]);
  649. // var object = threeDCards[cardIndex];
  650. // new TWEEN.Tween(object.position)
  651. // .to(
  652. // {
  653. // x: locates[iindex].x,
  654. // y: locates[iindex].y * Resolution,
  655. // z: 2000,
  656. // },
  657. // Math.random() * duration + duration
  658. // )
  659. // .easing(TWEEN.Easing.Exponential.InOut)
  660. // .start();
  661. // new TWEEN.Tween(object.rotation)
  662. // .to(
  663. // {
  664. // x: 0,
  665. // y: 0,
  666. // z: 0,
  667. // },
  668. // Math.random() * duration + duration
  669. // )
  670. // .easing(TWEEN.Easing.Exponential.InOut)
  671. // .start();
  672. // object.element.classList.add("prize");
  673. // });
  674. // new TWEEN.Tween(this)
  675. // .to({}, duration * 2)
  676. // .onUpdate(render)
  677. // .start()
  678. // .onComplete(() => {
  679. // // 动画结束后可以操作
  680. // setLotteryStatus();
  681. // });
  682. // }
  683. const threeContainer = ref(null);
  684. function selectCard(duration = 600) {
  685. return new Promise((resolve) => {
  686. const width = 160;
  687. // 计算总页数
  688. pageMaxIndex.value = Math.ceil(currentLuckys.length / cardsPerPage);
  689. pageIndex.value = 0;
  690. // 为每页计算位置信息
  691. const pageLocates = [];
  692. for (let page = 0; page < pageMaxIndex.value; page++) {
  693. const startIndex = page * cardsPerPage;
  694. const endIndex = Math.min(
  695. (page + 1) * cardsPerPage,
  696. currentLuckys.length
  697. );
  698. const pageCount = endIndex - startIndex;
  699. const pageLocate = [];
  700. // 根据当前页的人数决定排列方式
  701. if (pageCount > 5) {
  702. // 大于5个分两排显示(与抽10人时的排列相同)
  703. const yPosition = [-75, 120];
  704. const mid = Math.ceil(pageCount / 2);
  705. let tag = -(mid - 1) / 2;
  706. for (let i = 0; i < mid; i++) {
  707. pageLocate.push({
  708. x: tag * width,
  709. y: yPosition[0],
  710. });
  711. tag++;
  712. }
  713. tag = -(pageCount - mid - 1) / 2;
  714. for (let i = mid; i < pageCount; i++) {
  715. pageLocate.push({
  716. x: tag * width,
  717. y: yPosition[1],
  718. });
  719. tag++;
  720. }
  721. } else {
  722. // 小于等于5个一排显示(与抽不足10人时的排列相同)
  723. let tag = -(pageCount - 1) / 2;
  724. for (let i = 0; i < pageCount; i++) {
  725. pageLocate.push({
  726. x: tag * width,
  727. y: 0,
  728. });
  729. tag++;
  730. }
  731. }
  732. pageLocates.push(pageLocate);
  733. }
  734. // console.log("pageLocates calculated:", pageLocates);
  735. // 初始化所有卡片位置(隐藏超出第一页的卡片)
  736. selectedCardIndex.forEach((cardIndex, index) => {
  737. changeSelectedCard(cardIndex, currentLuckys[index]);
  738. const object = threeDCards[cardIndex];
  739. // 计算卡片应该在第几页
  740. const cardPage = Math.floor(index / cardsPerPage);
  741. const isVisible = cardPage === 0;
  742. // 计算在当前页中的索引
  743. const pageNowIndex = index % cardsPerPage;
  744. const pageLocate = pageLocates[cardPage][pageNowIndex];
  745. new TWEEN.Tween(object.position)
  746. .to(
  747. {
  748. x: isVisible ? pageLocate.x : pageLocate.x,
  749. y: isVisible ? pageLocate.y : pageLocate.y + 1000, // 隐藏的卡片移到下方
  750. z: 2100,
  751. },
  752. Math.random() * duration + duration
  753. )
  754. .easing(TWEEN.Easing.Exponential.InOut)
  755. .start();
  756. new TWEEN.Tween(object.rotation)
  757. .to(
  758. {
  759. x: 0,
  760. y: 0,
  761. z: 0,
  762. },
  763. Math.random() * duration + duration
  764. )
  765. .easing(TWEEN.Easing.Exponential.InOut)
  766. .start();
  767. object.element.classList.add("prize");
  768. });
  769. // 保存页面位置信息到全局变量,供switchPage使用
  770. window.pageLocates = pageLocates;
  771. new TWEEN.Tween({})
  772. .to({}, duration * 2)
  773. .onUpdate(() => render())
  774. .start()
  775. .onComplete(() => {
  776. console.log("selectCard animation completed");
  777. // 如果有多页,添加鼠标滚轮事件监听
  778. if (pageMaxIndex.value > 1) {
  779. // console.log("pageMaxIndex添加滚轮", pageMaxIndex.value);
  780. // 添加滚轮事件监听
  781. addWheelListener();
  782. }
  783. setLotteryStatus();
  784. resolve();
  785. });
  786. });
  787. }
  788. // 分页切换函数
  789. function switchPage(direction) {
  790. if (isPageTransitioning || pageMaxIndex.value <= 1) return;
  791. const newPage =
  792. direction === "next" ? pageIndex.value + 1 : pageIndex.value - 1;
  793. if (newPage < 0 || newPage >= pageMaxIndex.value) return;
  794. isPageTransitioning = true;
  795. const duration = 800;
  796. // 使用保存的页面位置信息
  797. const pageLocates = window.pageLocates;
  798. if (!pageLocates) {
  799. console.error("页面位置信息未找到");
  800. isPageTransitioning = false;
  801. return;
  802. }
  803. // 动画切换卡片位置
  804. globalCardIndexes.forEach((cardIndex, index) => {
  805. const object = threeDCards[cardIndex];
  806. const cardPage = Math.floor(index / cardsPerPage);
  807. const isVisible = cardPage === newPage;
  808. // 计算在当前页中的索引
  809. const pageNowIndex = index % cardsPerPage;
  810. const pageLocate = pageLocates[cardPage][pageNowIndex];
  811. // console.log(
  812. // "cardPage",
  813. // cardPage,
  814. // "pageNowIndex",
  815. // pageNowIndex,
  816. // "pageLocate",
  817. // pageLocate
  818. // );
  819. new TWEEN.Tween(object.position)
  820. .to(
  821. {
  822. x: pageLocate.x,
  823. y: isVisible ? pageLocate.y : pageLocate.y + 1000,
  824. z: 2100,
  825. },
  826. duration
  827. )
  828. .easing(TWEEN.Easing.Cubic.InOut)
  829. .start();
  830. });
  831. new TWEEN.Tween({})
  832. .to({}, duration)
  833. .onUpdate(() => render())
  834. .onComplete(() => {
  835. pageIndex.value = newPage;
  836. isPageTransitioning = false;
  837. console.log(
  838. `切换到第 ${pageIndex.value + 1} 页,共 ${pageMaxIndex.value}`
  839. );
  840. })
  841. .start();
  842. }
  843. // 鼠标滚轮事件处理
  844. function handleWheel(event) {
  845. if (isPageTransitioning || pageMaxIndex.value <= 1) return;
  846. event.preventDefault();
  847. if (event.deltaY > 0) {
  848. // 向下滚动,显示下一页
  849. switchPage("next");
  850. } else if (event.deltaY < 0) {
  851. // 向上滚动,显示上一页
  852. switchPage("prev");
  853. }
  854. }
  855. // 添加鼠标滚轮事件监听器
  856. function addWheelListener() {
  857. if (threeContainer.value) {
  858. threeContainer.value.addEventListener("wheel", handleWheel, {
  859. passive: false,
  860. });
  861. }
  862. }
  863. // 移除鼠标滚轮事件监听器
  864. function removeWheelListener() {
  865. if (threeContainer.value) {
  866. threeContainer.value.removeEventListener("wheel", handleWheel);
  867. }
  868. }
  869. /**
  870. * 重置抽奖牌内容
  871. */
  872. function resetCard(duration = 500) {
  873. if (currentLuckys.length === 0) {
  874. return Promise.resolve();
  875. }
  876. globalCardIndexes = [];
  877. // 移除鼠标滚轮事件监听器
  878. removeWheelListener();
  879. // 重置分页状态
  880. pageIndex.value = 0;
  881. pageMaxIndex.value = 0;
  882. isPageTransitioning = false;
  883. // 清理保存的页面位置信息
  884. if (window.pageLocates) {
  885. delete window.pageLocates;
  886. }
  887. selectedCardIndex.forEach((index) => {
  888. let object = threeDCards[index],
  889. target = targets.sphere[index];
  890. new TWEEN.Tween(object.position)
  891. .to(
  892. {
  893. x: target.position.x,
  894. y: target.position.y,
  895. z: target.position.z,
  896. },
  897. Math.random() * duration + duration
  898. )
  899. .easing(TWEEN.Easing.Exponential.InOut)
  900. .start();
  901. new TWEEN.Tween(object.rotation)
  902. .to(
  903. {
  904. x: target.rotation.x,
  905. y: target.rotation.y,
  906. z: target.rotation.z,
  907. },
  908. Math.random() * duration + duration
  909. )
  910. .easing(TWEEN.Easing.Exponential.InOut)
  911. .start();
  912. });
  913. return new Promise((resolve, reject) => {
  914. new TWEEN.Tween(this)
  915. .to({}, duration * 2)
  916. .onUpdate(render)
  917. .start()
  918. .onComplete(() => {
  919. selectedCardIndex.forEach((index) => {
  920. let object = threeDCards[index];
  921. object.element.classList.remove("prize");
  922. });
  923. resolve();
  924. });
  925. });
  926. }
  927. // 抽奖
  928. const lotteryBtn = () => {
  929. // console.log("isLotting", isLotting);
  930. // console.log("currentPrize.value", currentPrize.value);
  931. if (isLotting) {
  932. stopLottery();
  933. return;
  934. }
  935. if (!currentPrize.value.isLook) {
  936. addQipao("请先揭秘礼品。");
  937. return;
  938. }
  939. if (currentPrize.value.leftCount <= 0) {
  940. addQipao("该礼品已抽取完毕,请揭秘下一个礼品。");
  941. return;
  942. }
  943. getPrizeUsers.value = [];
  944. let params = {
  945. gradeName: currentPrize.value.gradeName,
  946. prizeName: currentPrize.value.prizeName,
  947. perWin: currentPrize.value.perWin,
  948. };
  949. // 异步调用API,不阻塞后续代码执行
  950. startLotteryApi(params)
  951. .then((res) => {
  952. // API返回结果时赋值
  953. getPrizeUsers.value = res.data || [];
  954. console.log("API返回结果:", res.data);
  955. })
  956. .catch((err) => {
  957. console.error("API调用失败:", err);
  958. getPrizeUsers.value = [];
  959. });
  960. setLotteryStatus(true);
  961. //更新剩余抽奖数目的数据显示
  962. changePrize();
  963. resetCard().then((res) => {
  964. // 抽奖
  965. lottery();
  966. });
  967. console.log("currentPrize", currentPrize.value);
  968. const text = "正在抽取[" + currentPrize.value.prizeName + "],调整好姿势";
  969. // addQipao(text);
  970. };
  971. /**
  972. * 抽奖
  973. */
  974. const lottery = () => {
  975. lotteryRef.value.innerHTML = "结束抽奖";
  976. rotateBall().then(() => {
  977. // 将之前的记录置空
  978. currentLuckys = [];
  979. selectedCardIndex = [];
  980. // 当前同时抽取的数目,当前奖品抽完还可以继续抽,但是不记录数据
  981. for (let i = 0; i < getPrizeUsers.value; i++) {
  982. currentLuckys.push(getPrizeUsers.value[i]);
  983. currentPrize.value.hasCount--;
  984. //避免中奖者出现在同一个卡片上
  985. let cardIndex = random(TOTAL_CARDS);
  986. while (selectedCardIndex.includes(cardIndex)) {
  987. cardIndex = random(TOTAL_CARDS);
  988. }
  989. selectedCardIndex.push(cardIndex);
  990. if (currentPrize.value.hasCount === 0) {
  991. break;
  992. }
  993. }
  994. selectCard(600);
  995. });
  996. };
  997. const stopLottery = () => {
  998. lotteryRef.value.innerHTML = "开始抽奖";
  999. rotateObj.stop();
  1000. };
  1001. const changePrize = async () => {
  1002. let type = currentPrize.value.type;
  1003. prizes.value[type].leftCount =
  1004. prizes.value[type].leftCount - EACH_COUNT[type] < 0
  1005. ? 0
  1006. : prizes.value[type].leftCount - EACH_COUNT[type];
  1007. await nextTick();
  1008. // 修改左侧prize的数目和百分比
  1009. setPrizeData(currentPrizeIndex);
  1010. };
  1011. /**
  1012. * 随机抽奖
  1013. */
  1014. function random(num) {
  1015. // Math.floor取到0-num-1之间数字的概率是相等的
  1016. return Math.floor(Math.random() * num);
  1017. }
  1018. /**
  1019. * 切换名牌人员信息
  1020. */
  1021. function changeCard(cardIndex, user) {
  1022. let card = threeDCards[cardIndex].element;
  1023. card.innerHTML = `<div class="name">${user.jwcode}</div>`;
  1024. }
  1025. function changeSelectedCard(cardIndex, user) {
  1026. // 保存到全局变量数组
  1027. if (!globalCardIndexes.includes(cardIndex)) {
  1028. globalCardIndexes.push(cardIndex);
  1029. }
  1030. let card = threeDCards[cardIndex].element;
  1031. card.innerHTML = `<div class="name">${user[1]}</div>`;
  1032. }
  1033. /**
  1034. * 切换名牌背景
  1035. */
  1036. function changeBackground(cardIndex, color) {
  1037. let card = threeDCards[cardIndex].element;
  1038. // card.style.backgroundColor =
  1039. // color || "rgba(255,170,22," + (Math.random() * 0.7 + 0.25) + ")";
  1040. card.style.backgroundColor = color || "rgba(255,170,22,1)";
  1041. card.style.border = "2px solid rgba(255, 255, 255, 1)";
  1042. }
  1043. /**
  1044. * 随机切换背景和人员信息
  1045. */
  1046. function shineCard() {
  1047. let maxCard = 10;
  1048. let maxUser;
  1049. let shineCard = 10 + random(maxCard);
  1050. setInterval(() => {
  1051. // 正在抽奖停止闪烁
  1052. if (isLotting) {
  1053. return;
  1054. }
  1055. maxUser = users.value.length;
  1056. for (let i = 0; i < shineCard; i++) {
  1057. let index = random(maxUser);
  1058. let cardIndex = random(TOTAL_CARDS);
  1059. // 当前显示的已抽中名单不进行随机切换
  1060. if (selectedCardIndex.includes(cardIndex)) {
  1061. continue;
  1062. }
  1063. changeBackground(cardIndex);
  1064. changeCard(cardIndex, users.value[index]);
  1065. }
  1066. }, 500);
  1067. }
  1068. const createHighlight = () => {
  1069. let text = "0123";
  1070. let step = 5;
  1071. let xoffset = 1;
  1072. let yoffset = 1;
  1073. let highlight = [];
  1074. text.split("").forEach((n) => {
  1075. highlight = highlight.concat(
  1076. NUMBER_MATRIX[n].map((item) => {
  1077. return `${item[0] + xoffset}-${item[1] + yoffset}`;
  1078. })
  1079. );
  1080. xoffset += step;
  1081. });
  1082. return highlight;
  1083. };
  1084. // 音乐播放器初始化函数
  1085. const initMusicPlayer = () => {
  1086. const musicBox = document.querySelector("#musicBox");
  1087. if (musicBox) {
  1088. // 1秒后自动尝试播放
  1089. setTimeout(() => {
  1090. musicPlay();
  1091. }, 1000);
  1092. }
  1093. };
  1094. const musicPlay = () => {
  1095. console.log("musicPlay 方法被调用");
  1096. const music = document.querySelector("#music");
  1097. const musicBox = document.querySelector("#musicBox");
  1098. if (!music || !musicBox) {
  1099. console.error("音乐元素未找到");
  1100. return;
  1101. }
  1102. if (music.paused) {
  1103. music
  1104. .play()
  1105. .then(() => {
  1106. console.log("音乐开始播放");
  1107. startRotateAnimation(musicBox);
  1108. })
  1109. .catch((error) => {
  1110. console.error("音乐播放失败:", error);
  1111. });
  1112. } else {
  1113. console.log("音乐暂停");
  1114. music.pause();
  1115. stopRotateAnimation();
  1116. }
  1117. };
  1118. // 旋转动画管理
  1119. let rotationState = { rotated: 0, stopAnimate: false, animationId: null };
  1120. const startRotateAnimation = (element) => {
  1121. rotationState.stopAnimate = false;
  1122. function animate() {
  1123. if (rotationState.stopAnimate) return;
  1124. rotationState.rotated = (rotationState.rotated + 1) % 360;
  1125. element.style.transform = `rotate(${rotationState.rotated}deg)`;
  1126. rotationState.animationId = requestAnimationFrame(animate);
  1127. }
  1128. animate();
  1129. };
  1130. const stopRotateAnimation = () => {
  1131. rotationState.stopAnimate = true;
  1132. if (rotationState.animationId) {
  1133. cancelAnimationFrame(rotationState.animationId);
  1134. rotationState.animationId = null;
  1135. }
  1136. };
  1137. function onWindowResize() {
  1138. camera.aspect = window.innerWidth / window.innerHeight;
  1139. camera.updateProjectionMatrix();
  1140. renderer.setSize(window.innerWidth, window.innerHeight);
  1141. render();
  1142. }
  1143. onMounted(async () => {
  1144. initAll();
  1145. initMusicPlayer();
  1146. window.addEventListener("resize", onWindowResize, false);
  1147. });
  1148. </script>
  1149. <style>
  1150. html,
  1151. body,
  1152. #app {
  1153. height: 100vh;
  1154. width: 100vw;
  1155. }
  1156. .homepage {
  1157. width: 100%;
  1158. height: 100%;
  1159. display: flex;
  1160. background-image: url("../../../assets/img/bg.png");
  1161. background-repeat: no-repeat;
  1162. background-size: 100% 100%;
  1163. justify-content: center;
  1164. }
  1165. .hllogo {
  1166. position: absolute;
  1167. right: 0;
  1168. bottom: 0;
  1169. }
  1170. .qipao {
  1171. width: 100%;
  1172. z-index: 100;
  1173. position: absolute;
  1174. top: 27vh;
  1175. left: 50%;
  1176. transform: translateX(-50%);
  1177. color: #db5c58;
  1178. font-weight: bold;
  1179. font-size: 42px;
  1180. width: auto;
  1181. text-align: center;
  1182. display: none;
  1183. white-space: nowrap;
  1184. }
  1185. a {
  1186. color: #ffffff;
  1187. }
  1188. .none {
  1189. display: none;
  1190. }
  1191. #container {
  1192. z-index: 3;
  1193. position: relative;
  1194. overflow: hidden;
  1195. }
  1196. .three-container {
  1197. width: 100%;
  1198. height: 100%;
  1199. }
  1200. /* 分页指示器样式 */
  1201. .page-indicator {
  1202. position: absolute;
  1203. bottom: 10%;
  1204. left: 50%;
  1205. transform: translateX(-50%);
  1206. z-index: 1000;
  1207. background: rgba(0, 0, 0, 0.7);
  1208. color: white;
  1209. padding: 8px 16px;
  1210. border-radius: 20px;
  1211. font-size: 14px;
  1212. pointer-events: none;
  1213. }
  1214. .rightPage {
  1215. width: 75px;
  1216. position: fixed;
  1217. transform: rotate(270deg);
  1218. top: 50%;
  1219. right: 15%;
  1220. display: none;
  1221. z-index: 999;
  1222. }
  1223. .leftPage {
  1224. width: 75px;
  1225. position: fixed;
  1226. transform: rotate(90deg);
  1227. top: 50%;
  1228. left: 23%;
  1229. display: none;
  1230. z-index: 999;
  1231. }
  1232. .canvas-box {
  1233. /* background-color: rgb(214, 0, 0); */
  1234. position: fixed;
  1235. left: 0;
  1236. top: 0;
  1237. z-index: -1;
  1238. }
  1239. #info {
  1240. position: absolute;
  1241. width: 100%;
  1242. color: #ffffff;
  1243. padding: 5px;
  1244. font-family: Monospace;
  1245. font-size: 13px;
  1246. font-weight: bold;
  1247. text-align: center;
  1248. z-index: 1;
  1249. }
  1250. #menu {
  1251. z-index: 4;
  1252. position: absolute;
  1253. bottom: 1vh;
  1254. width: 100%;
  1255. display: flex;
  1256. justify-content: center;
  1257. align-items: center;
  1258. }
  1259. .element {
  1260. width: 15vh;
  1261. height: 19vh;
  1262. /* box-shadow: 0 0 12px rgba(0, 255, 255, 0.5); */
  1263. border: 1px solid rgba(127, 255, 255, 0.25);
  1264. text-align: center;
  1265. cursor: default;
  1266. transition: background-color 0.3s ease-in;
  1267. }
  1268. .element:hover {
  1269. box-shadow: 0 0 12px rgba(255, 168, 38, 0.75);
  1270. border: 1px solid rgba(255, 255, 255, 1);
  1271. }
  1272. .element .name {
  1273. font-size: 2.9vh;
  1274. font-weight: bold;
  1275. color: rgba(255, 255, 255, 1);
  1276. /* text-shadow: 0 0 1vh rgba(0, 255, 255, 0.95); */
  1277. }
  1278. .btn {
  1279. background-image: url("../../../assets/img/抽奖按钮.png");
  1280. background-color: transparent;
  1281. background-repeat: no-repeat;
  1282. background-size: 100% 100%;
  1283. width: 180px;
  1284. height: 70px;
  1285. color: #fff;
  1286. border: 0;
  1287. font-size: 2.5vh;
  1288. font-weight: bold;
  1289. cursor: pointer;
  1290. text-align: center;
  1291. }
  1292. .highlight {
  1293. background: linear-gradient(360deg, #e23d26, #f49c27) !important;
  1294. box-shadow: 0 0 12px rgba(253, 105, 0, 0.95);
  1295. border: 2px solid rgba(255, 255, 255, 1);
  1296. }
  1297. .highlight.element .name {
  1298. text-shadow: 0 0 16px rgba(255, 255, 255, 0.95);
  1299. }
  1300. .prize.element .name {
  1301. text-shadow: none;
  1302. }
  1303. .prize.element {
  1304. transition: background-color 1.5s ease-in 0.3s;
  1305. background: linear-gradient(360deg, #e23d26, #f49c27) !important;
  1306. border: 2px solid rgba(255, 255, 255, 1);
  1307. }
  1308. .prize .company,
  1309. .prize .details,
  1310. .prize .name,
  1311. .highlight .company,
  1312. .highlight .name,
  1313. .highlight .details {
  1314. color: rgba(255, 255, 255, 0.85);
  1315. }
  1316. .dan-mu {
  1317. visibility: hidden;
  1318. position: fixed;
  1319. z-index: -1;
  1320. font-size: 12px;
  1321. top: 1vh;
  1322. left: 0;
  1323. padding: 0 1.2vh;
  1324. height: 2.2vh;
  1325. line-height: 2.2vh;
  1326. border-radius: 1vh;
  1327. box-sizing: border-box;
  1328. background-color: rgba(0, 127, 127, 0.37);
  1329. box-shadow: 0 0 4px rgba(0, 255, 255, 0.5);
  1330. border: 1px solid rgba(127, 255, 255, 0.25);
  1331. color: rgba(127, 255, 255, 0.75);
  1332. }
  1333. .dan-mu.active {
  1334. visibility: visible;
  1335. }
  1336. .leftBar {
  1337. position: fixed;
  1338. left: 0;
  1339. padding-left: 1.2vh;
  1340. top: 15vh;
  1341. z-index: 100;
  1342. }
  1343. #prizeBar {
  1344. max-height: 60vh;
  1345. width: 330px;
  1346. overflow-x: hidden;
  1347. overflow-y: auto;
  1348. /* 隐藏滚动条 */
  1349. scrollbar-width: none; /* Firefox */
  1350. -ms-overflow-style: none; /* IE */
  1351. }
  1352. #prizeBar::-webkit-scrollbar {
  1353. display: none; /* Chrome, Safari */
  1354. }
  1355. .prize-list {
  1356. margin: 0;
  1357. padding: 0;
  1358. list-style: none;
  1359. }
  1360. .prize-item {
  1361. padding: 2px;
  1362. display: flex;
  1363. align-items: center;
  1364. justify-content: center;
  1365. flex-wrap: nowrap;
  1366. /* color: rgba(127, 255, 255, 0.75); */
  1367. width: 30vh;
  1368. height: 9.5vh;
  1369. box-sizing: border-box;
  1370. transition: transform 1s ease-in;
  1371. }
  1372. .getPrizeName {
  1373. color: #d5291f;
  1374. font-weight: bold;
  1375. font-size: 24px;
  1376. width: 27vh;
  1377. height: 5vh;
  1378. border: 2px solid rgb(255, 255, 255);
  1379. border-radius: 5px;
  1380. display: flex;
  1381. flex-direction: column;
  1382. justify-content: center;
  1383. align-items: center;
  1384. background-color: #ffd283;
  1385. opacity: 0.8;
  1386. margin-left: 2vh;
  1387. margin-top: 3.5vh;
  1388. cursor: pointer;
  1389. position: relative;
  1390. }
  1391. .dgetPrizeName {
  1392. color: #d5291f;
  1393. font-weight: bold;
  1394. font-size: 24px;
  1395. width: 27vh;
  1396. height: 60vh;
  1397. border: 2px solid rgb(255, 255, 255);
  1398. border-radius: 5px;
  1399. display: flex;
  1400. flex-direction: column;
  1401. /* justify-content: center; */
  1402. align-items: center;
  1403. background-color: #ffd283;
  1404. opacity: 0.8;
  1405. margin-left: 2vh;
  1406. margin-top: 3.5vh;
  1407. position: relative;
  1408. }
  1409. .open {
  1410. position: absolute;
  1411. top: -42px;
  1412. animation: bounce1 2s ease-in-out infinite;
  1413. transform: rotate(180deg);
  1414. }
  1415. .close {
  1416. position: absolute;
  1417. top: -32px;
  1418. animation: bounce2 2s ease-in-out infinite;
  1419. cursor: pointer;
  1420. }
  1421. @keyframes bounce1 {
  1422. 0%,
  1423. 100% {
  1424. transform: rotate(180deg) translateY(0);
  1425. }
  1426. 50% {
  1427. transform: rotate(180deg) translateY(-8px);
  1428. }
  1429. }
  1430. @keyframes bounce2 {
  1431. 0%,
  1432. 100% {
  1433. transform: translateY(0);
  1434. }
  1435. 50% {
  1436. transform: translateY(-8px);
  1437. }
  1438. }
  1439. .tableHead {
  1440. width: 100%;
  1441. display: flex;
  1442. justify-content: center;
  1443. font-size: 18px;
  1444. /* gap: 80px; */
  1445. margin-bottom: 10px;
  1446. position: absolute;
  1447. top: 10px;
  1448. }
  1449. .tableHead1 {
  1450. width: 50%;
  1451. text-align: center;
  1452. }
  1453. .tableHead2 {
  1454. width: 50%;
  1455. text-align: center;
  1456. }
  1457. .gradient-line {
  1458. width: 90%;
  1459. height: 3px;
  1460. border-radius: 150%;
  1461. background: linear-gradient(
  1462. to right,
  1463. transparent 0%,
  1464. #d5291f 45%,
  1465. #d5291f 55%,
  1466. transparent 100%
  1467. );
  1468. position: absolute;
  1469. bottom: -10px;
  1470. }
  1471. .tableBody {
  1472. display: flex;
  1473. margin-top: 50px;
  1474. width: 100%;
  1475. height: 54vh;
  1476. justify-content: center;
  1477. overflow-y: auto; /* 启用垂直滚动 */
  1478. overflow-x: hidden; /* 启用垂直滚动 */
  1479. /* 隐藏滚动条 */
  1480. scrollbar-width: none; /* Firefox */
  1481. -ms-overflow-style: none; /* IE */
  1482. }
  1483. .tableBody::-webkit-scrollbar {
  1484. display: none; /* Chrome, Safari */
  1485. }
  1486. .tableItem {
  1487. display: flex;
  1488. font-size: 18px;
  1489. width: 27vh;
  1490. margin-bottom: 10px;
  1491. top: 10px;
  1492. }
  1493. .tableItem1 {
  1494. width: 50%;
  1495. text-align: center;
  1496. }
  1497. .tableItem2 {
  1498. width: 50%;
  1499. text-align: center;
  1500. }
  1501. .readyLook {
  1502. width: 100%;
  1503. height: 100%;
  1504. }
  1505. .prize-item .prize-img {
  1506. width: 7.5vh;
  1507. height: 7.5vh;
  1508. margin: auto 10px;
  1509. border-radius: 50%;
  1510. background-color: #fff;
  1511. text-shadow: 0 0 1vh rgba(0, 255, 255, 0.95);
  1512. overflow: hidden;
  1513. }
  1514. .prize-img img {
  1515. width: 90%;
  1516. height: 90%;
  1517. position: relative;
  1518. top: 50%;
  1519. left: 50%;
  1520. transform: translate(-50%, -50%);
  1521. }
  1522. .prize-text {
  1523. padding: 0 5px;
  1524. width: 100%;
  1525. height: 100%;
  1526. /* margin: auto 0; */
  1527. flex: 1;
  1528. display: flex;
  1529. flex-direction: column;
  1530. justify-content: center;
  1531. /* gap: 4px; */
  1532. }
  1533. .prize-title {
  1534. margin: 4px 0;
  1535. font-size: 1.4vh;
  1536. height: 30%;
  1537. border: 1px solid #e13726;
  1538. border-radius: 20px;
  1539. background-color: #ffffff;
  1540. display: flex;
  1541. color: #d5291f;
  1542. font-weight: bold;
  1543. display: flex;
  1544. align-items: center;
  1545. }
  1546. .level {
  1547. width: 38%;
  1548. height: 100%;
  1549. margin-right: 2%;
  1550. border-radius: 20px;
  1551. background: linear-gradient(360deg, #e23d26, #f49c27) !important;
  1552. color: #fff;
  1553. display: flex;
  1554. justify-content: center;
  1555. align-items: center;
  1556. }
  1557. .prize-count {
  1558. padding: 4px 0;
  1559. position: relative;
  1560. }
  1561. .prize-count .progress {
  1562. height: 1.8vh;
  1563. background: rgba(0, 0, 0, 0.5);
  1564. padding: 1px;
  1565. overflow: visible;
  1566. border-radius: 1vh;
  1567. }
  1568. .progress .progress-bar {
  1569. border-radius: 1.8vh;
  1570. position: relative;
  1571. animation: animate-positive 2s;
  1572. background-color: #d9534f;
  1573. height: 1.8vh;
  1574. -webkit-transition: width 0.6s ease;
  1575. -o-transition: width 0.6s ease;
  1576. transition: width 0.6s ease;
  1577. }
  1578. .progress-bar.active {
  1579. animation: reverse progress-bar-stripes 0.4s linear infinite,
  1580. animate-positive 2s;
  1581. }
  1582. .progress-bar-striped {
  1583. background-image: -webkit-linear-gradient(
  1584. 45deg,
  1585. rgba(255, 255, 255, 0.15) 25%,
  1586. transparent 25%,
  1587. transparent 50%,
  1588. rgba(255, 255, 255, 0.15) 50%,
  1589. rgba(255, 255, 255, 0.15) 75%,
  1590. transparent 75%,
  1591. transparent
  1592. );
  1593. background-image: -o-linear-gradient(
  1594. 45deg,
  1595. rgba(255, 255, 255, 0.15) 25%,
  1596. transparent 25%,
  1597. transparent 50%,
  1598. rgba(255, 255, 255, 0.15) 50%,
  1599. rgba(255, 255, 255, 0.15) 75%,
  1600. transparent 75%,
  1601. transparent
  1602. );
  1603. background-image: linear-gradient(
  1604. 45deg,
  1605. rgba(255, 255, 255, 0.15) 25%,
  1606. transparent 25%,
  1607. transparent 50%,
  1608. rgba(255, 255, 255, 0.15) 50%,
  1609. rgba(255, 255, 255, 0.15) 75%,
  1610. transparent 75%,
  1611. transparent
  1612. );
  1613. -webkit-background-size: 8px 8px;
  1614. background-size: 8px 8px;
  1615. }
  1616. @-webkit-keyframes animate-positive {
  1617. 0% {
  1618. width: 0;
  1619. }
  1620. }
  1621. @keyframes animate-positive {
  1622. 0% {
  1623. width: 0;
  1624. }
  1625. }
  1626. @-webkit-keyframes progress-bar-stripes {
  1627. from {
  1628. background-position: 8px 0;
  1629. }
  1630. to {
  1631. background-position: 0 0;
  1632. }
  1633. }
  1634. @-o-keyframes progress-bar-stripes {
  1635. from {
  1636. background-position: 8px 0;
  1637. }
  1638. to {
  1639. background-position: 0 0;
  1640. }
  1641. }
  1642. @keyframes progress-bar-stripes {
  1643. from {
  1644. background-position: 8px 0;
  1645. }
  1646. to {
  1647. background-position: 0 0;
  1648. }
  1649. }
  1650. .prize-count-left {
  1651. position: absolute;
  1652. color: #fff;
  1653. right: 35%;
  1654. font-size: 1.8vh;
  1655. line-height: 1.6vh;
  1656. top: 50%;
  1657. transform: translateY(-50%);
  1658. }
  1659. .shine {
  1660. background-color: #ffd283;
  1661. border: 1px solid rgba(255, 255, 255, 1);
  1662. border-radius: 5px;
  1663. box-shadow: 0 0 15px 0 #ffd283;
  1664. transform: scale(1.1);
  1665. transform-origin: left center;
  1666. position: relative;
  1667. overflow: hidden;
  1668. }
  1669. .done {
  1670. position: relative;
  1671. }
  1672. .done:after {
  1673. content: "";
  1674. position: absolute;
  1675. top: 0;
  1676. left: 0;
  1677. right: 0;
  1678. bottom: 0;
  1679. background-color: #ffd283;
  1680. border: 2px solid rgba(255, 255, 255, 1);
  1681. border-radius: 5px;
  1682. opacity: 0.3;
  1683. cursor: not-allowed;
  1684. }
  1685. .shine span {
  1686. position: absolute;
  1687. display: block;
  1688. }
  1689. .shine span:nth-child(1) {
  1690. top: 0;
  1691. left: 0;
  1692. width: 100%;
  1693. height: 2px;
  1694. background: linear-gradient(90deg, transparent, #f46303);
  1695. animation: animate1 1s linear infinite;
  1696. }
  1697. @keyframes animate1 {
  1698. 0% {
  1699. left: -100%;
  1700. }
  1701. 50%,
  1702. 100% {
  1703. left: 100%;
  1704. }
  1705. }
  1706. .shine span:nth-child(2) {
  1707. top: -100%;
  1708. right: 0;
  1709. width: 2px;
  1710. height: 100%;
  1711. background: linear-gradient(180deg, transparent, #f46303);
  1712. animation: animate2 1s linear infinite;
  1713. animation-delay: 0.25s;
  1714. }
  1715. @keyframes animate2 {
  1716. 0% {
  1717. top: -100%;
  1718. }
  1719. 50%,
  1720. 100% {
  1721. top: 100%;
  1722. }
  1723. }
  1724. .shine span:nth-child(3) {
  1725. bottom: 0;
  1726. right: 0;
  1727. width: 100%;
  1728. height: 2px;
  1729. background: linear-gradient(270deg, transparent, #f46303);
  1730. animation: animate3 1s linear infinite;
  1731. animation-delay: 0.5s;
  1732. }
  1733. @keyframes animate3 {
  1734. 0% {
  1735. right: -100%;
  1736. }
  1737. 50%,
  1738. 100% {
  1739. right: 100%;
  1740. }
  1741. }
  1742. .shine span:nth-child(4) {
  1743. bottom: -100%;
  1744. left: 0;
  1745. width: 2px;
  1746. height: 100%;
  1747. background: linear-gradient(360deg, transparent, #f46303);
  1748. animation: animate4 1s linear infinite;
  1749. animation-delay: 0.75s;
  1750. }
  1751. @keyframes animate4 {
  1752. 0% {
  1753. bottom: -100%;
  1754. }
  1755. 50%,
  1756. 100% {
  1757. bottom: 100%;
  1758. }
  1759. }
  1760. .shine.prize-item {
  1761. /* width: 24vh; */
  1762. margin: 1.2vh 0;
  1763. }
  1764. .prize-mess {
  1765. color: #fff;
  1766. line-height: 5vh;
  1767. font-size: 1.6vh;
  1768. margin: 2.4vh 0;
  1769. }
  1770. .music {
  1771. position: fixed;
  1772. top: 3vh;
  1773. right: 4vh;
  1774. z-index: 5;
  1775. }
  1776. .music-item {
  1777. display: block !important;
  1778. opacity: 0;
  1779. }
  1780. .music-box {
  1781. width: 5vh;
  1782. height: 5vh;
  1783. border-radius: 50%;
  1784. text-align: center;
  1785. line-height: 5vh;
  1786. font-size: 1.4vh;
  1787. color: #fff;
  1788. cursor: pointer;
  1789. background-color: rgba(253, 105, 0, 0.9);
  1790. border: 1px solid rgba(255, 255, 255, 0.5);
  1791. }
  1792. .rotate-active {
  1793. animation: rotate 4s linear infinite;
  1794. }
  1795. @keyframes rotate {
  1796. from {
  1797. transform: rotate(0);
  1798. }
  1799. to {
  1800. transform: rotate(360deg);
  1801. }
  1802. }
  1803. .margin-l-40 {
  1804. margin-left: 40px;
  1805. }
  1806. .fixed-bar {
  1807. position: fixed;
  1808. bottom: 20px;
  1809. right: 20px;
  1810. }
  1811. .fixed-btn {
  1812. margin: 20px 0 0;
  1813. width: 200px;
  1814. text-align: center;
  1815. display: block;
  1816. }
  1817. #lottery {
  1818. animation: breath 1.6s linear infinite;
  1819. /* box-shadow: 0px 0px 15px rgb(127 255 255 / 75%); */
  1820. }
  1821. @keyframes breath {
  1822. 0% {
  1823. transform: scale(1);
  1824. opacity: 0.8;
  1825. }
  1826. 25% {
  1827. transform: scale(1.1);
  1828. opacity: 1;
  1829. }
  1830. 50% {
  1831. transform: scale(1);
  1832. opacity: 1;
  1833. }
  1834. 75% {
  1835. transform: scale(0.9);
  1836. opacity: 1;
  1837. }
  1838. 100% {
  1839. transform: scale(1);
  1840. opacity: 0.8;
  1841. }
  1842. }
  1843. </style>