76 lines
2.6 KiB
Vue
76 lines
2.6 KiB
Vue
<template>
|
||
<view class="find-spot">
|
||
<view class="scene" :style="{ background: scene.color + '55' }">
|
||
<view
|
||
v-for="(it, i) in items"
|
||
:key="it.id"
|
||
class="spot"
|
||
:style="pos(i)"
|
||
:class="{ win: foundIdx === i }"
|
||
@click="tap(it, i)"
|
||
>
|
||
<view class="spot-icon">{{ propVisual(it.name).emoji }}</view>
|
||
</view>
|
||
<view class="scene-emoji">{{ scene.emoji }}</view>
|
||
</view>
|
||
<view class="hint">场景里藏着线索,点一点找到它!</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { propVisual, sceneVisual } from '../../utils/visual.js'
|
||
import { playSound } from '../../utils/sound.js'
|
||
|
||
export default {
|
||
name: 'FindSpot',
|
||
props: { config: { type: String, default: '' } },
|
||
data() { return { retries: 0, found: false, done: false, foundIdx: -1 } },
|
||
computed: {
|
||
cfg() {
|
||
try { return JSON.parse(this.config) } catch (e) { return { kind: 'find_spot', items: [], answer: 0 } }
|
||
},
|
||
items() { return this.cfg.items || [] },
|
||
scene() { return sceneVisual('场景') }
|
||
},
|
||
methods: {
|
||
propVisual,
|
||
pos(i) {
|
||
// 5 个固定散布位置(左上/右上/中/左下/右下),按 index 轮转
|
||
const spots = [
|
||
{ left: '8%', top: '18%' }, { left: '58%', top: '12%' }, { left: '30%', top: '45%' },
|
||
{ left: '10%', top: '62%' }, { left: '55%', top: '60%' }
|
||
]
|
||
return spots[i % spots.length]
|
||
},
|
||
tap(it, i) {
|
||
if (this.found || this.done) return
|
||
if (it.id === this.cfg.answer) {
|
||
this.found = true
|
||
this.done = true
|
||
this.foundIdx = i
|
||
playSound('good')
|
||
this.$emit('done', { success: true })
|
||
} else {
|
||
this.retries++
|
||
playSound('bad')
|
||
if (this.retries >= 2) {
|
||
this.done = true
|
||
this.$emit('done', { success: false })
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.scene { position: relative; height: 420rpx; border-radius: 28rpx; margin-bottom: 24rpx; }
|
||
.scene-emoji { position: absolute; right: 20rpx; top: 10rpx; font-size: 120rpx; opacity: .35; }
|
||
.spot { position: absolute; width: 100rpx; height: 100rpx; border-radius: 24rpx; background: #fff; box-shadow: 0 4rpx 14rpx rgba(91,70,54,.18); display: flex; align-items: center; justify-content: center; transition: transform .12s ease; }
|
||
.spot:active { transform: scale(.92); }
|
||
.spot.win { transform: scale(1.08); border: 4rpx solid #ffb347; animation: win-pulse .5s ease; }
|
||
.spot-icon { font-size: 52rpx; }
|
||
.hint { text-align: center; color: #a08c74; }
|
||
@keyframes win-pulse { 0% { transform: scale(.9); } 50% { transform: scale(1.15); } 100% { transform: scale(1.08); } }
|
||
</style>
|