blindModel.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <template>
  2. <Theme>
  3. <Popup ref="popRef" mode="center">
  4. <template #content>
  5. <view class="content">
  6. <view class="content_success" v-if="giftStatus == 'await'">
  7. <trans _t="恭喜!您有一份盲盒待解锁" class="title"></trans>
  8. <view class="gift_container">
  9. <image
  10. src="/static/user/gift_bg.png"
  11. class="gift_box_img"
  12. ></image>
  13. </view>
  14. <view class="gift_bottom"></view>
  15. </view>
  16. <view class="opening_animation" v-else-if="giftStatus == 'loading'">
  17. <view class="gift_container opening">
  18. <image
  19. src="/static/user/gift_bg.png"
  20. class="gift_box_img opening"
  21. ></image>
  22. <view class="crack_effect">
  23. <view
  24. class="crack"
  25. v-for="i in 4"
  26. :key="i"
  27. :style="{ '--delay': i * 0.1 + 's' }"
  28. ></view>
  29. </view>
  30. <view class="explosion_particles">
  31. <view
  32. class="explosion"
  33. v-for="i in 12"
  34. :key="i"
  35. :style="{
  36. '--delay': i * 0.05 + 's',
  37. '--angle': i * 30 + 'deg',
  38. }"
  39. ></view>
  40. </view>
  41. </view>
  42. <image
  43. class="fireworks_img"
  44. src="/static/user/fireworks.gif"
  45. ></image>
  46. </view>
  47. <view class="content_success" v-else-if="giftStatus == 'open'">
  48. <trans _t="恭喜!您获得" class="title"></trans>
  49. <view class="gift_box">
  50. <image src="/static/user/get_gift.png" class="img"></image>
  51. <view class="cont_box">
  52. <view class="title">
  53. <trans _t="盲盒兑换券" />
  54. ×1</view
  55. >
  56. <view class="time">
  57. <trans _t="有效期限:" />
  58. <view class="days">
  59. 180
  60. <trans _t="天" />
  61. </view>
  62. </view>
  63. </view>
  64. </view>
  65. </view>
  66. </view>
  67. </template>
  68. <template #footer>
  69. <slot name="footer">
  70. <view class="footer">
  71. <template v-if="giftStatus == 'await'">
  72. <view class="submit_btn" @click="openGift">
  73. <trans _t="立即打开" />
  74. </view>
  75. <view class="submit_btn submit_shop" @click="close">
  76. <trans _t="一会儿再说" />
  77. </view>
  78. </template>
  79. <view
  80. class="submit_btn"
  81. @click="gitGift"
  82. v-else-if="giftStatus == 'open'"
  83. >
  84. <trans _t="领取" />
  85. </view>
  86. </view>
  87. </slot>
  88. </template>
  89. </Popup>
  90. <floatButton
  91. bottom="120px"
  92. right="0"
  93. image="/static/user/get_gift.png"
  94. :unclaimedNum="count"
  95. @onConfirm="open"
  96. v-if="!giftShow && count"
  97. />
  98. </Theme>
  99. </template>
  100. <script setup>
  101. import { ref, watch, computed, nextTick } from "vue";
  102. import Popup from "@/components/popup.vue";
  103. import { t } from "@/locale";
  104. import { useSystemStore } from "@/store";
  105. import { CONTENT_RECEIVE_BOX } from "@/api";
  106. import { Toast, TimeOut } from "@/utils";
  107. import floatButton from "@/components/floatButton";
  108. const popRef = ref(null);
  109. const useSystem = useSystemStore();
  110. const count = computed(() => useSystem.getCount);
  111. const giftShow = ref(false);
  112. const giftStatus = ref("await");
  113. const emit = defineEmits(["complete", "close", "open"]);
  114. const open = () => {
  115. giftStatus.value = "await";
  116. popRef.value && popRef.value.open();
  117. giftShow.value = true;
  118. emit("open");
  119. };
  120. const close = () => {
  121. emit("close");
  122. giftShow.value = false;
  123. popRef.value && popRef.value.close();
  124. };
  125. const openGift = async () => {
  126. giftStatus.value = "loading";
  127. try {
  128. await CONTENT_RECEIVE_BOX();
  129. useSystem.setBoxes();
  130. giftStatus.value = "open";
  131. } catch (error) {
  132. Toast(error.msg);
  133. giftStatus.value = "await";
  134. }
  135. // 添加开启动画的延迟,让用户看到完整的动画效果
  136. // setTimeout(async () => {
  137. // }, 2000);
  138. };
  139. const gitGift = () => {
  140. uni.navigateTo({ url: "/pages/user/weal_center" });
  141. giftStatus.value = "await";
  142. close();
  143. };
  144. watch(
  145. () => count.value,
  146. (val) => {
  147. if (giftStatus.value == "await") {
  148. if (val) {
  149. nextTick(() => {
  150. open();
  151. });
  152. } else {
  153. close();
  154. }
  155. }
  156. },
  157. {
  158. immediate: true,
  159. }
  160. );
  161. defineExpose({ open, close });
  162. </script>
  163. <style lang="less" scoped>
  164. @import url("@/style.less");
  165. // 关键帧动画定义
  166. @keyframes giftFloat {
  167. 0%,
  168. 100% {
  169. transform: translateY(0px) rotate(0deg);
  170. }
  171. 25% {
  172. transform: translateY(-10rpx) rotate(1deg);
  173. }
  174. 50% {
  175. transform: translateY(-5rpx) rotate(0deg);
  176. }
  177. 75% {
  178. transform: translateY(-15rpx) rotate(-1deg);
  179. }
  180. }
  181. @keyframes shine {
  182. 0% {
  183. transform: translateX(-100%) translateY(-100%) rotate(45deg);
  184. }
  185. 50% {
  186. transform: translateX(100%) translateY(100%) rotate(45deg);
  187. }
  188. 100% {
  189. transform: translateX(100%) translateY(100%) rotate(45deg);
  190. }
  191. }
  192. @keyframes particleFloat {
  193. 0%,
  194. 100% {
  195. transform: translateY(0px) scale(1);
  196. opacity: 0.8;
  197. }
  198. 50% {
  199. transform: translateY(-20rpx) scale(1.2);
  200. opacity: 1;
  201. }
  202. }
  203. @keyframes giftShake {
  204. 0%,
  205. 100% {
  206. transform: translateX(0) scale(1);
  207. }
  208. 10% {
  209. transform: translateX(-5rpx) scale(1.05);
  210. }
  211. 20% {
  212. transform: translateX(5rpx) scale(1.05);
  213. }
  214. 30% {
  215. transform: translateX(-5rpx) scale(1.05);
  216. }
  217. 40% {
  218. transform: translateX(5rpx) scale(1.05);
  219. }
  220. 50% {
  221. transform: translateX(-5rpx) scale(1.05);
  222. }
  223. 60% {
  224. transform: translateX(5rpx) scale(1.05);
  225. }
  226. 70% {
  227. transform: translateX(-5rpx) scale(1.05);
  228. }
  229. 80% {
  230. transform: translateX(5rpx) scale(1.05);
  231. }
  232. 90% {
  233. transform: translateX(-5rpx) scale(1.05);
  234. }
  235. }
  236. @keyframes giftExplode {
  237. 0% {
  238. transform: scale(1.1);
  239. opacity: 1;
  240. }
  241. 50% {
  242. transform: scale(1.3);
  243. opacity: 0.8;
  244. }
  245. 100% {
  246. transform: scale(0.8);
  247. opacity: 0;
  248. }
  249. }
  250. @keyframes crackAppear {
  251. 0% {
  252. width: 0;
  253. opacity: 0;
  254. }
  255. 100% {
  256. width: var(--width, 80%);
  257. opacity: 1;
  258. }
  259. }
  260. @keyframes explosionFly {
  261. 0% {
  262. transform: rotate(var(--angle)) translateX(0) scale(1);
  263. opacity: 1;
  264. }
  265. 50% {
  266. transform: rotate(var(--angle)) translateX(100rpx) scale(1.5);
  267. opacity: 0.8;
  268. }
  269. 100% {
  270. transform: rotate(var(--angle)) translateX(200rpx) scale(0.5);
  271. opacity: 0;
  272. }
  273. }
  274. :deep(.conts) {
  275. min-width: 85vw;
  276. max-width: 95vw;
  277. }
  278. .content {
  279. padding: 40rpx 0;
  280. flex: 1;
  281. .flex_center();
  282. &_success {
  283. position: relative;
  284. .flex_center();
  285. flex-direction: column;
  286. gap: 20rpx;
  287. .gift_bottom {
  288. position: absolute;
  289. left: 50%;
  290. bottom: 10rpx;
  291. width: 180rpx;
  292. height: 40rpx;
  293. border-radius: 20rpx;
  294. background: #ffdbbb;
  295. transform: translateX(-90rpx);
  296. filter: blur(40rpx);
  297. }
  298. .gift_box {
  299. display: flex;
  300. justify-content: space-between;
  301. align-items: center;
  302. width: 100%;
  303. border-radius: 20rpx;
  304. padding: 10rpx 16rpx 0;
  305. background: linear-gradient(180deg, #e6734c 0%, #eb3a68 100%);
  306. .img {
  307. width: 140rpx;
  308. height: 140rpx;
  309. margin-bottom: 20rpx;
  310. }
  311. .cont_box {
  312. display: flex;
  313. flex-direction: column;
  314. justify-content: space-between;
  315. align-items: center;
  316. width: 450rpx;
  317. height: 222rpx;
  318. background: url("/static/user/gift_box.png");
  319. background-size: 100% 100%;
  320. padding: 40rpx 0 50rpx;
  321. .title {
  322. .size(32rpx);
  323. color: var(--red);
  324. }
  325. .time {
  326. display: flex;
  327. .size(28rpx);
  328. color: var(--text-02);
  329. .days {
  330. color: var(--text);
  331. font-weight: 700;
  332. }
  333. }
  334. }
  335. }
  336. }
  337. .title {
  338. .size(32rpx);
  339. color: #3d3d3d;
  340. font-weight: 600;
  341. margin-bottom: 40rpx;
  342. }
  343. .img {
  344. width: 300rpx;
  345. height: 300rpx;
  346. }
  347. // 盲盒容器
  348. .gift_container {
  349. position: relative;
  350. width: 300rpx;
  351. height: 300rpx;
  352. display: flex;
  353. align-items: center;
  354. justify-content: center;
  355. .gift_box_img {
  356. width: 300rpx;
  357. height: 300rpx;
  358. transition: all 0.3s ease;
  359. animation: giftFloat 2s ease-in-out infinite;
  360. &.opening {
  361. animation: giftShake 0.5s ease-in-out, giftExplode 0.8s ease-out 0.5s;
  362. transform: scale(1.1);
  363. }
  364. }
  365. // 拆开时的裂纹效果
  366. .crack_effect {
  367. position: absolute;
  368. top: 0;
  369. left: 0;
  370. width: 100%;
  371. height: 100%;
  372. .crack {
  373. position: absolute;
  374. background: linear-gradient(45deg, transparent, #ff6b35, transparent);
  375. animation: crackAppear 0.3s ease-out;
  376. animation-delay: var(--delay);
  377. &:nth-child(1) {
  378. top: 20%;
  379. left: 10%;
  380. width: 80%;
  381. height: 2rpx;
  382. transform: rotate(15deg);
  383. }
  384. &:nth-child(2) {
  385. top: 40%;
  386. left: 20%;
  387. width: 60%;
  388. height: 2rpx;
  389. transform: rotate(-25deg);
  390. }
  391. &:nth-child(3) {
  392. top: 60%;
  393. left: 15%;
  394. width: 70%;
  395. height: 2rpx;
  396. transform: rotate(35deg);
  397. }
  398. &:nth-child(4) {
  399. top: 80%;
  400. left: 25%;
  401. width: 50%;
  402. height: 2rpx;
  403. transform: rotate(-15deg);
  404. }
  405. }
  406. }
  407. // 爆炸粒子效果
  408. .explosion_particles {
  409. position: absolute;
  410. top: 50%;
  411. left: 50%;
  412. transform: translate(-50%, -50%);
  413. .explosion {
  414. position: absolute;
  415. width: 6rpx;
  416. height: 6rpx;
  417. background: radial-gradient(circle, #ff6b35, #ffd700);
  418. border-radius: 50%;
  419. animation: explosionFly 1s ease-out forwards;
  420. animation-delay: var(--delay);
  421. transform: rotate(var(--angle)) translateX(0);
  422. }
  423. }
  424. }
  425. // 开启动画容器
  426. .opening_animation {
  427. position: relative;
  428. display: flex;
  429. align-items: center;
  430. justify-content: center;
  431. width: 100%;
  432. height: 300rpx;
  433. .fireworks_img {
  434. position: absolute;
  435. top: 50%;
  436. left: 50%;
  437. transform: translate(-50%, -50%);
  438. width: 400rpx;
  439. height: 400rpx;
  440. z-index: 10;
  441. }
  442. }
  443. }
  444. .footer {
  445. .submit_btn {
  446. width: 100%;
  447. height: 76rpx;
  448. padding: 16rpx 30rpx;
  449. background-color: var(--black);
  450. color: var(--light);
  451. .flex_center();
  452. border-radius: 16rpx;
  453. .size(24rpx);
  454. margin-top: 24rpx;
  455. &.submit_shop {
  456. background-color: var(--bg);
  457. border: 1rpx solid var(--black);
  458. color: var(--black);
  459. }
  460. }
  461. }
  462. </style>