Commit b480ffa9 authored by 董政锦's avatar 董政锦

fix: game4 修复苹果手机加速度计需要交互点击触摸一下才开启功能;

parent 848c80fd
......@@ -166,7 +166,7 @@ function handleRoomBack() {
}
const touchHandler = () => {
tick.value += 1
tick.value += 5
submitScore()
}
......@@ -247,7 +247,8 @@ onBeforeUnmount(() => {
</template>
<LoadingView v-if="currentView === 'loading'" :image-urls="imageUrls" :user-join-status="userJoinStatus"
@touchGameRule="showGameRuleHandler" />
<PlayingView v-else-if="currentView === 'playing'" :image-urls="imageUrls" :tick="tick" :rank="rank"
<!-- <PlayingView v-else :image-urls="imageUrls" :tick="tick" :rank="rank" -->
<PlayingView v-else-if="currentView === 'playing'" :image-urls="imageUrls" :tick="tick" :rank="rank"
:currentHorse="currentHorse" :currentYaoyiyao="currentYaoyiyao" :countdown-interval="countdownInterval"
:is-div-desc-visible="isDivDescVisible" @touch="touchHandler" />
<ScoreView v-else :image-urls="imageUrls" :tick="tick" :level="level" @replay="replayGame"
......
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue'
import { onBeforeUnmount, onMounted, ref } from 'vue'
import shakeSound from '@/assets/images/game4/yaoyiyao.mp3'
const props = defineProps<{
......@@ -16,6 +16,10 @@ const emit = defineEmits<{
touch: []
}>()
// ========== iOS 设备检测 ==========
const isIOS = typeof (DeviceMotionEvent as any).requestPermission === 'function'
const showIOSHint = ref(isIOS)
// ========== 摇一摇 ==========
const shakeCooldown = 300
let lastShakeTime = 0
......@@ -33,6 +37,37 @@ let wasShaking = false
let audioContext: AudioContext | null = null
let shakeAudioBuffer: AudioBuffer | null = null
// iOS 13+/iPadOS 需要主动请求 DeviceMotion 权限
let motionPermissionGranted = false
// 摇一摇图片抖动动画状态
const isYaoyiyaoShaking = ref(false)
let yaoyiyaoShakeTimer: ReturnType<typeof setTimeout> | null = null
const requestMotionPermission = async (): Promise<boolean> => {
// 检查是否需要权限请求(iOS 12.2+)
if (typeof (DeviceMotionEvent as any).requestPermission === 'function') {
try {
const state = await (DeviceMotionEvent as any).requestPermission()
if (state === 'granted') {
console.log('[摇一摇] iOS DeviceMotion 权限已授予')
motionPermissionGranted = true
// 权限拿到后再注册监听
window.addEventListener('devicemotion', handleDeviceMotion)
return true
}
console.warn('[摇一摇] iOS DeviceMotion 权限被拒绝:', state)
return false
} catch (err) {
console.error('[摇一摇] iOS 权限请求失败:', err)
return false
}
}
// Android / 旧版 iOS:无需额外权限
motionPermissionGranted = true
return true
}
const initAudio = async () => {
try {
audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
......@@ -82,6 +117,13 @@ const handleDeviceMotion = (ev: DeviceMotionEvent) => {
if (now - lastShakeTime < shakeCooldown) return
lastShakeTime = now
// 摇一摇图片抖动动画
isYaoyiyaoShaking.value = true
if (yaoyiyaoShakeTimer) clearTimeout(yaoyiyaoShakeTimer)
yaoyiyaoShakeTimer = setTimeout(() => {
isYaoyiyaoShaking.value = false
}, 320)
playShakeSound()
// 摇晃时打印日志:输出将要通过 websocket 提交的数据
......@@ -105,8 +147,6 @@ const handleDeviceMotion = (ev: DeviceMotionEvent) => {
}
}
window.addEventListener('devicemotion', handleDeviceMotion)
// ========== 生命周期 ==========
onMounted(async () => {
......@@ -115,6 +155,15 @@ onMounted(async () => {
} catch (error) {
console.error('初始化失败:', error)
}
// 非 iOS 设备直接注册监听;iOS 需等用户点击后通过 requestMotionPermission 注册
if (typeof (DeviceMotionEvent as any).requestPermission !== 'function') {
window.addEventListener('devicemotion', handleDeviceMotion)
motionPermissionGranted = true
console.log('[摇一摇] DeviceMotion 监听已启动(Android/旧版iOS)')
} else {
console.log('[摇一摇] 等待用户点击以请求 iOS DeviceMotion 权限…')
}
})
onBeforeUnmount(() => {
......@@ -122,7 +171,20 @@ onBeforeUnmount(() => {
if (audioContext) {
audioContext.close()
}
if (yaoyiyaoShakeTimer) {
clearTimeout(yaoyiyaoShakeTimer)
}
})
// 点击马触发:iOS 先请求权限 + 触发射分
const onTapHorse = async () => {
if (!motionPermissionGranted) {
await requestMotionPermission()
// 权限请求完成(无论结果)隐藏 iOS 提示
showIOSHint.value = false
}
emit('touch')
}
</script>
<template>
......@@ -141,9 +203,19 @@ onBeforeUnmount(() => {
<div>{{ tick }}</div>
<div>当前排名: &nbsp;&nbsp;<label>{{ rank }}</label>&nbsp;</div>
</div>
<!-- iOS 权限提示蒙层 -->
<Transition name="ios-hint-fade">
<div v-if="showIOSHint" class="ios-hint-overlay" @click="onTapHorse">
<div class="ios-hint-content">
<div class="ios-hint-title">👇点击下方赛马</div>
<div class="ios-hint-desc">启用摇一摇功能开始游戏</div>
<div class="ios-hint-arrow"></div>
</div>
</div>
</Transition>
<!-- <div class="bg-bottom"></div> -->
<div class="bg-yaoyiyao" :style="{ background: `${currentYaoyiyao} center/cover` }"></div>
<div class="play-horse" :style="{ background: `${currentHorse} center/cover` }" @click="$emit('touch')"></div>
<div class="bg-yaoyiyao" :class="{ 'is-shaking': isYaoyiyaoShaking }" :style="{ background: `${currentYaoyiyao} center/cover` }"></div>
<div class="play-horse" :class="{ 'horse-clickable': showIOSHint }" :style="{ background: `${currentHorse} center/cover` }" @click="onTapHorse"></div>
</div>
</template>
......@@ -297,14 +369,98 @@ onBeforeUnmount(() => {
width: 514.51px;
height: 229px;
transform: translateX(-50%);
transition: transform 0.08s ease-out;
}
/* 摇一摇图片抖动动画 */
.bg-yaoyiyao.is-shaking {
animation: yaoyiyao-shake 0.32s ease-in-out;
}
@keyframes yaoyiyao-shake {
0% { transform: translateX(-50%) rotate(0deg); }
15% { transform: translateX(-50%) rotate(-8deg); }
30% { transform: translateX(-50%) rotate(8deg); }
45% { transform: translateX(-50%) rotate(-6deg); }
60% { transform: translateX(-50%) rotate(6deg); }
75% { transform: translateX(-50%) rotate(-3deg); }
90% { transform: translateX(-50%) rotate(3deg); }
100% { transform: translateX(-50%) rotate(0deg); }
}
.play-horse {
position: absolute;
bottom: 164px;
left: 50%;
width: 597px;
height: 679px;
/* transform: translateX(-50%) scale(2.4); */
transform: translateX(-50%);
}
/* 马匹点击引导动画 - 脉冲呼吸效果 */
.horse-clickable {
animation: horse-pulse 1.2s ease-in-out infinite;
}
@keyframes horse-pulse {
0%, 100% { filter: brightness(1) drop-shadow(0 0 8px rgba(255, 215, 0, 0)); }
50% { filter: brightness(1.12) drop-shadow(0 0 24px rgba(255, 215, 0, 0.6)); }
}
/* ===== iOS 权限提示蒙层 ===== */
.ios-hint-overlay {
position: absolute;
top: 0;
bottom: 0;
left: var(--stage-viewport-left, 0);
width: var(--stage-viewport-width, 750px);
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.55);
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.ios-hint-content {
text-align: center;
color: #fff;
animation: ios-hint-bounce 1.6s ease-in-out infinite;
}
.ios-hint-title {
font-size: 36pt;
font-weight: bold;
margin-bottom: 16px;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
.ios-hint-desc {
font-size: 22pt;
opacity: 0.85;
margin-bottom: 20px;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}
.ios-hint-arrow {
font-size: 40pt;
animation: ios-arrow-bounce 0.8s ease-in-out infinite alternate;
}
@keyframes ios-hint-bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
}
@keyframes ios-arrow-bounce {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(16px); opacity: 0.3; }
}
.ios-hint-fade-enter-active {
transition: opacity 0.4s ease;
}
.ios-hint-fade-leave-active {
transition: opacity 0.3s ease;
}
.ios-hint-fade-enter-from,
.ios-hint-fade-leave-to {
opacity: 0;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment