169 lines
2.9 KiB
Vue
169 lines
2.9 KiB
Vue
<script setup>
|
||
import { ref, onMounted } from 'vue'
|
||
|
||
// 组件属性
|
||
const props = defineProps({
|
||
// 是否可见
|
||
visible: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
// x范围(总宽度)
|
||
xRange: {
|
||
type: Number,
|
||
default: 630
|
||
},
|
||
// y范围(总高度)
|
||
yRange: {
|
||
type: Number,
|
||
default: 400
|
||
},
|
||
// y起始位置
|
||
yStart: {
|
||
type: Number,
|
||
default: 350
|
||
},
|
||
// 福字宽度
|
||
fuWidth: {
|
||
type: Number,
|
||
default: 100
|
||
},
|
||
// 福字高度
|
||
fuHeight: {
|
||
type: Number,
|
||
default: 100
|
||
}
|
||
})
|
||
|
||
// 组件事件
|
||
const emit = defineEmits(['click'])
|
||
|
||
// 福字位置
|
||
const position = ref({
|
||
x: 0,
|
||
y: 0
|
||
})
|
||
|
||
// 计算随机位置
|
||
const calculateRandomPosition = () => {
|
||
position.value = {
|
||
x: Math.random() * (props.xRange - props.fuWidth),
|
||
y: Math.random() * (props.yRange - props.fuHeight) + props.yStart
|
||
}
|
||
}
|
||
|
||
// 点击处理
|
||
const handleClick = () => {
|
||
emit('click')
|
||
}
|
||
|
||
// 组件挂载时计算位置
|
||
onMounted(() => {
|
||
calculateRandomPosition()
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div
|
||
v-if="visible"
|
||
class="fu-click-area"
|
||
:style="{
|
||
left: `${position.x}rpx`,
|
||
top: `${position.y}rpx`,
|
||
width: `${fuWidth}rpx`,
|
||
height: `${fuHeight}rpx`
|
||
}"
|
||
@click="handleClick"
|
||
>
|
||
<img src="/static/images/icon_fu.png" alt="福字" class="fu-icon" />
|
||
<img src="/static/images/icon_hand.png" alt="点击手势" class="hand-icon" />
|
||
<div class="click-indicator">
|
||
<div class="pulse-circle"></div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* 福字点击区域 */
|
||
.fu-click-area {
|
||
position: absolute;
|
||
cursor: pointer;
|
||
z-index: 25;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.fu-icon {
|
||
position: absolute;
|
||
width: 94rpx;
|
||
height: 74rpx;
|
||
top: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
margin-bottom: 20rpx;
|
||
animation: sway 3s infinite ease-in-out;
|
||
transform-origin: center center;
|
||
}
|
||
|
||
@keyframes sway {
|
||
0%, 100% {
|
||
transform: translateX(-50%) rotate(0deg);
|
||
}
|
||
25% {
|
||
transform: translateX(-50%) rotate(-3deg);
|
||
}
|
||
75% {
|
||
transform: translateX(-50%) rotate(3deg);
|
||
}
|
||
}
|
||
|
||
.hand-icon {
|
||
position: absolute;
|
||
width: 38rpx;
|
||
height: 40rpx;
|
||
top: 70%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
animation: clickUp 2s infinite ease-in-out;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@keyframes clickUp {
|
||
0%, 100% {
|
||
transform: translate(-50%, -50%) scale(1.2);
|
||
}
|
||
50% {
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
}
|
||
|
||
.click-indicator {
|
||
position: absolute;
|
||
top: 30rpx;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
}
|
||
|
||
.pulse-circle {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
background-color: rgba(255, 215, 0, 0.3);
|
||
border: 2rpx solid rgba(255, 215, 0, 0.6);
|
||
animation: pulse 2s infinite;
|
||
animation-delay: 1s;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% {
|
||
transform: scale(0.8);
|
||
opacity: 0.8;
|
||
}
|
||
100% {
|
||
transform: scale(2);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
</style>
|