bindbox-mini/components/BlessingPopup.vue

268 lines
5.4 KiB
Vue

<template>
<view v-if="visible" class="blessing-popup-mask" @tap="handleClose">
<view class="blessing-popup-content" @tap.stop>
<view class="blessing-emoji">{{ currentBlessing.emoji }}</view>
<view v-if="currentBlessing.type === 'sheep'" class="blessing-subtitle">小羊祝你</view>
<view class="blessing-text">
<text v-for="(char, index) in currentBlessing.chars"
:key="index"
class="char"
:class="{ 'from-left': index % 2 === 0, 'from-right': index % 2 === 1 }"
:style="{ animationDelay: index * 0.15 + 's' }">
{{ char }}
</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { registerBlessing, unregisterBlessing } from '@/utils/blessing.js'
const visible = ref(false)
const blessings = [
{
emoji: '🐏',
chars: ['三', '羊', '开', '泰'],
type: 'sheep'
},
{
emoji: '🐴',
chars: ['马', '到', '功', '成'],
type: 'horse'
}
]
const currentBlessing = ref(blessings[0])
const handleShow = ({ type }) => {
console.log('[BlessingPopup] handleShow 被调用, type:', type)
// 根据类型选择祝福语
let index = 0
if (type === 'random') {
index = Math.floor(Math.random() * blessings.length)
} else if (type === 'sheep') {
index = 0
} else if (type === 'horse') {
index = 1
} else {
index = Math.floor(Math.random() * blessings.length)
}
currentBlessing.value = blessings[index]
visible.value = true
console.log('[BlessingPopup] 显示祝福:', currentBlessing.value)
// 3秒后自动隐藏
setTimeout(() => {
visible.value = false
console.log('[BlessingPopup] 隐藏祝福')
}, 3000)
}
const handleClose = () => {
visible.value = false
console.log('[BlessingPopup] 用户点击关闭')
}
onMounted(() => {
console.log('[BlessingPopup] 组件已挂载,准备注册监听器')
registerBlessing(handleShow)
console.log('[BlessingPopup] 监听器注册完成')
})
onUnmounted(() => {
console.log('[BlessingPopup] 组件即将卸载')
unregisterBlessing()
})
</script>
<style lang="scss" scoped>
.blessing-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 10000; // 比支付弹窗(999)高
display: flex;
align-items: center;
justify-content: center;
animation: maskFadeIn 0.3s ease-out;
}
@keyframes maskFadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.blessing-popup-content {
width: 600rpx;
max-width: 90%;
background: rgba(255, 255, 255, 0.98);
border-radius: 32rpx;
padding: 60rpx 40rpx;
text-align: center;
animation: popupScaleIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20rpx);
}
@keyframes popupScaleIn {
0% {
opacity: 0;
transform: scale(0.8);
}
60% {
transform: scale(1.05);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.blessing-emoji {
font-size: 140rpx;
line-height: 1;
margin-bottom: 24rpx;
display: block;
}
// 小羊动画 - 弹跳出现
.blessing-popup-content:has(.blessing-subtitle) .blessing-emoji {
animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
// 小马动画 - 从左边跑步进场
.blessing-popup-content:not(:has(.blessing-subtitle)) .blessing-emoji {
animation: emojiRun 0.8s ease-out;
}
@keyframes emojiBounce {
0% {
transform: scale(0) rotate(-180deg);
opacity: 0;
}
50% {
transform: scale(1.2) rotate(10deg);
}
100% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
}
@keyframes emojiRun {
0% {
transform: translateX(-300rpx) scale(0.8);
opacity: 0;
}
60% {
transform: translateX(30rpx) scale(1.1);
}
80% {
transform: translateX(-15rpx) scale(0.95);
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
.blessing-subtitle {
font-size: 32rpx;
color: #FF9500;
font-weight: 700;
margin-bottom: 16rpx;
opacity: 0;
animation: subtitleFadeIn 0.5s ease-out 0.3s forwards;
}
@keyframes subtitleFadeIn {
from {
opacity: 0;
transform: translateY(10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.blessing-text {
display: flex;
justify-content: center;
gap: 16rpx;
margin-top: 24rpx;
.char {
font-size: 56rpx;
font-weight: 900;
color: #FF6B00;
text-shadow: 0 4rpx 12rpx rgba(255, 107, 0, 0.3);
opacity: 0;
animation: charAppear 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
// 从左边出现
.char.from-left {
animation-name: charAppearFromLeft;
}
// 从右边出现
.char.from-right {
animation-name: charAppearFromRight;
}
}
@keyframes charAppear {
0% {
opacity: 0;
transform: translateY(30rpx) scale(0.5);
}
60% {
transform: translateY(-8rpx) scale(1.1);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes charAppearFromLeft {
0% {
opacity: 0;
transform: translateX(-80rpx) scale(0.5);
}
60% {
transform: translateX(10rpx) scale(1.1);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
@keyframes charAppearFromRight {
0% {
opacity: 0;
transform: translateX(80rpx) scale(0.5);
}
60% {
transform: translateX(-10rpx) scale(1.1);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
</style>