bindbox-mini/components/activity/DrawLoadingPopup.vue
2026-01-02 20:40:22 +08:00

320 lines
7.0 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view v-if="visible" class="draw-loading-overlay" @touchmove.stop.prevent>
<!-- 背景渐变 -->
<view class="bg-gradient"></view>
<!-- 光圈效果 -->
<view class="light-ring"></view>
<view class="light-ring ring-2"></view>
<!-- 主内容 -->
<view class="loading-content">
<!-- 3D礼盒动画 -->
<view class="gift-container">
<view class="gift-box">
<view class="gift-lid">
<view class="lid-top"></view>
<view class="lid-ribbon"></view>
</view>
<view class="gift-body">
<view class="body-ribbon"></view>
</view>
</view>
<!-- 闪光粒子 -->
<view class="sparkle sparkle-1"></view>
<view class="sparkle sparkle-2"></view>
<view class="sparkle sparkle-3"></view>
<view class="sparkle sparkle-4">💫</view>
</view>
<!-- 文字区域 -->
<view class="text-area">
<text class="loading-title">{{ title }}</text>
<view class="loading-dots">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</view>
<!-- 进度条当有多次抽奖时显示 -->
<view v-if="total > 1" class="progress-area">
<view class="progress-bar">
<view class="progress-fill" :style="{ width: progressPercent + '%' }"></view>
</view>
<text class="progress-text">{{ progress }} / {{ total }}</text>
</view>
<!-- 提示文字 -->
<text class="tip-text">请稍候好运即将到来...</text>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
visible: { type: Boolean, default: false },
title: { type: String, default: '努力拆盒中' },
progress: { type: Number, default: 0 },
total: { type: Number, default: 1 }
})
const progressPercent = computed(() => {
if (props.total <= 0) return 0
return Math.min(100, Math.round((props.progress / props.total) * 100))
})
</script>
<style lang="scss" scoped>
.draw-loading-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* 背景 */
.bg-gradient {
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center,
rgba(255, 140, 0, 0.15) 0%,
rgba(30, 20, 50, 0.98) 50%,
rgba(10, 5, 20, 0.99) 100%
);
}
/* 光圈 */
.light-ring {
position: absolute;
width: 500rpx; height: 500rpx;
border: 4rpx solid rgba(255, 200, 100, 0.3);
border-radius: 50%;
animation: ringExpand 2s ease-out infinite;
}
.ring-2 {
animation-delay: 1s;
}
@keyframes ringExpand {
0% {
transform: scale(0.5);
opacity: 0.8;
border-width: 8rpx;
}
100% {
transform: scale(2);
opacity: 0;
border-width: 2rpx;
}
}
/* 主内容 */
.loading-content {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
z-index: 10;
animation: contentPop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes contentPop {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
/* 礼盒容器 */
.gift-container {
position: relative;
width: 240rpx;
height: 240rpx;
margin-bottom: 60rpx;
}
/* 礼盒动画 */
.gift-box {
position: absolute;
left: 50%; top: 50%;
transform: translate(-50%, -50%);
animation: boxBounce 1.5s ease-in-out infinite;
}
@keyframes boxBounce {
0%, 100% { transform: translate(-50%, -50%); }
50% { transform: translate(-50%, -60%); }
}
.gift-lid {
position: relative;
animation: lidShake 1.5s ease-in-out infinite;
transform-origin: center bottom;
}
@keyframes lidShake {
0%, 100% { transform: rotate(0deg) translateY(0); }
25% { transform: rotate(-5deg) translateY(-10rpx); }
75% { transform: rotate(5deg) translateY(-10rpx); }
}
.lid-top {
width: 140rpx; height: 30rpx;
background: linear-gradient(135deg, #FF6B35, #FF8C00);
border-radius: 8rpx 8rpx 0 0;
box-shadow: 0 -4rpx 16rpx rgba(255, 107, 53, 0.5);
}
.lid-ribbon {
position: absolute;
left: 50%; top: -20rpx;
transform: translateX(-50%);
width: 40rpx; height: 50rpx;
background: linear-gradient(135deg, #FFD700, #FFA500);
border-radius: 8rpx;
&::before, &::after {
content: '';
position: absolute;
top: 36rpx;
width: 30rpx; height: 30rpx;
background: linear-gradient(135deg, #FFD700, #FFA500);
border-radius: 50%;
}
&::before { left: -20rpx; }
&::after { right: -20rpx; }
}
.gift-body {
width: 120rpx; height: 100rpx;
background: linear-gradient(135deg, #FF8C00, #FF6B35);
border-radius: 0 0 12rpx 12rpx;
margin: 0 auto;
margin-top: -2rpx;
box-shadow:
0 12rpx 32rpx rgba(255, 107, 53, 0.4),
inset 0 -10rpx 20rpx rgba(0,0,0,0.1);
}
.body-ribbon {
width: 30rpx; height: 100%;
background: linear-gradient(180deg, #FFD700, #FFA500);
margin: 0 auto;
}
/* 闪光粒子 */
.sparkle {
position: absolute;
font-size: 32rpx;
animation: sparkleFloat 2s ease-in-out infinite;
}
.sparkle-1 { top: 10rpx; left: 20rpx; animation-delay: 0s; }
.sparkle-2 { top: 30rpx; right: 20rpx; animation-delay: 0.5s; }
.sparkle-3 { bottom: 40rpx; left: 0; animation-delay: 1s; }
.sparkle-4 { bottom: 20rpx; right: 10rpx; animation-delay: 1.5s; }
@keyframes sparkleFloat {
0%, 100% {
opacity: 0.4;
transform: translateY(0) scale(0.8);
}
50% {
opacity: 1;
transform: translateY(-20rpx) scale(1.2);
}
}
/* 文字区域 */
.text-area {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 40rpx;
}
.loading-title {
font-size: 40rpx;
font-weight: 800;
color: #FFF;
text-shadow: 0 0 30rpx rgba(255, 180, 100, 0.8);
letter-spacing: 4rpx;
}
.loading-dots {
display: flex;
gap: 8rpx;
}
.dot {
width: 12rpx; height: 12rpx;
background: #FFD700;
border-radius: 50%;
animation: dotBounce 1.4s ease-in-out infinite;
&:nth-child(1) { animation-delay: 0s; }
&:nth-child(2) { animation-delay: 0.2s; }
&:nth-child(3) { animation-delay: 0.4s; }
}
@keyframes dotBounce {
0%, 80%, 100% {
transform: scale(0.6);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
/* 进度条 */
.progress-area {
width: 400rpx;
margin-bottom: 30rpx;
}
.progress-bar {
height: 16rpx;
background: rgba(255, 255, 255, 0.15);
border-radius: 8rpx;
overflow: hidden;
box-shadow: inset 0 2rpx 4rpx rgba(0,0,0,0.2);
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #FFD700, #FF8C00, #FF6B35);
border-radius: 8rpx;
transition: width 0.3s ease-out;
box-shadow: 0 0 16rpx rgba(255, 200, 0, 0.6);
}
.progress-text {
display: block;
text-align: center;
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
margin-top: 12rpx;
font-weight: 600;
}
/* 提示文字 */
.tip-text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
letter-spacing: 2rpx;
}
</style>