234 lines
4.8 KiB
Vue
234 lines
4.8 KiB
Vue
<template>
|
|
<view v-if="visible" class="blessing-float" :class="{ 'hide': shouldHide }" @tap="handleClose">
|
|
<view class="blessing-content">
|
|
<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 shouldHide = ref(false)
|
|
const blessings = [
|
|
{
|
|
emoji: '🐏',
|
|
chars: ['三', '羊', '开', '泰'],
|
|
type: 'sheep'
|
|
},
|
|
{
|
|
emoji: '🐴',
|
|
chars: ['马', '到', '功', '成'],
|
|
type: 'horse'
|
|
}
|
|
]
|
|
const currentBlessing = ref(blessings[0])
|
|
|
|
const handleShow = ({ type }) => {
|
|
console.log('[BlessingFloat] 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
|
|
shouldHide.value = false
|
|
|
|
console.log('[BlessingFloat] 显示祝福:', currentBlessing.value)
|
|
|
|
// 3秒后自动隐藏
|
|
setTimeout(() => {
|
|
shouldHide.value = true
|
|
setTimeout(() => {
|
|
visible.value = false
|
|
console.log('[BlessingFloat] 隐藏祝福')
|
|
}, 300)
|
|
}, 3000)
|
|
}
|
|
|
|
const handleClose = () => {
|
|
shouldHide.value = true
|
|
setTimeout(() => {
|
|
visible.value = false
|
|
console.log('[BlessingFloat] 用户点击关闭')
|
|
}, 300)
|
|
}
|
|
|
|
onMounted(() => {
|
|
console.log('[BlessingFloat] 组件已挂载,准备注册监听器')
|
|
registerBlessing(handleShow)
|
|
console.log('[BlessingFloat] 监听器注册完成')
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
console.log('[BlessingFloat] 组件即将卸载')
|
|
unregisterBlessing()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.blessing-float {
|
|
position: fixed;
|
|
top: 200rpx;
|
|
right: 30rpx;
|
|
z-index: 99999;
|
|
animation: floatIn 0.5s ease-out;
|
|
transition: all 0.3s ease;
|
|
|
|
&.hide {
|
|
opacity: 0;
|
|
transform: translateY(-50rpx) scale(0.8);
|
|
}
|
|
}
|
|
|
|
@keyframes floatIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-50rpx) scale(0.8);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
}
|
|
}
|
|
|
|
.blessing-content {
|
|
background: linear-gradient(135deg, #FF6B00 0%, #FF9500 100%);
|
|
padding: 24rpx 20rpx;
|
|
border-radius: 20rpx;
|
|
box-shadow: 0 8rpx 24rpx rgba(255, 107, 0, 0.4);
|
|
text-align: center;
|
|
min-width: 200rpx;
|
|
backdrop-filter: blur(10rpx);
|
|
}
|
|
|
|
.blessing-emoji {
|
|
font-size: 60rpx;
|
|
line-height: 1;
|
|
margin-bottom: 8rpx;
|
|
display: block;
|
|
animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
}
|
|
|
|
@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;
|
|
}
|
|
}
|
|
|
|
.blessing-subtitle {
|
|
font-size: 20rpx;
|
|
color: #FFFFFF;
|
|
font-weight: 700;
|
|
margin-bottom: 6rpx;
|
|
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: 8rpx;
|
|
margin-top: 8rpx;
|
|
|
|
.char {
|
|
font-size: 32rpx;
|
|
font-weight: 900;
|
|
color: #FFFFFF;
|
|
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
|
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(20rpx) scale(0.5);
|
|
}
|
|
60% {
|
|
transform: translateY(-4rpx) scale(1.1);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
}
|
|
}
|
|
|
|
@keyframes charAppearFromLeft {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateX(-40rpx) scale(0.5);
|
|
}
|
|
60% {
|
|
transform: translateX(6rpx) scale(1.1);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateX(0) scale(1);
|
|
}
|
|
}
|
|
|
|
@keyframes charAppearFromRight {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateX(40rpx) scale(0.5);
|
|
}
|
|
60% {
|
|
transform: translateX(-6rpx) scale(1.1);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateX(0) scale(1);
|
|
}
|
|
}
|
|
</style>
|