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

style: game4 和 game5调整;

parent 40a1a7b0
{
"clawMode.mode": "editor"
}
\ No newline at end of file
......@@ -308,7 +308,7 @@ const rankCloseHandler = () => {
<div class="row row-3">
<div class="col-1">{{ rank > 0 ? rank : '未上榜' }}</div>
<div class="col-2">
<div class="avatar" :style="{ backgroundImage: `url(${avatar})` }"></div>
<div class="avatar" :style="{ backgroundImage: `url(${avatar})`,top: '27px' }"></div>
<div>{{ nickname }}</div>
</div>
<div class="col-3">{{ tick }}</div>
......
......@@ -20,11 +20,11 @@ onMounted(() => {
<div class="game-desc" @click="$emit('touchGameRule')">游戏规则</div>
<div class="img-logo"></div>
<div class="img-title"></div>
<div class="img-avatar" :style="`background: url(${avatar});border-radius:66px;width:132px;height:132px;`">
</div>
<!-- <div class="img-avatar" :style="`background: url(${avatar});border-radius:66px;width:132px;height:132px;`">
</div> -->
<div class="txt-contdown">游戏等待开始倒计时&nbsp;<b style="font-weight: 900;">60</b>&nbsp;</div>
<!-- <div class="txt-contdown"></div> -->
<div class="txt-nickname">{{ nickname }}</div>
<!-- <div class="txt-nickname">{{ nickname }}</div> -->
<div class="txt-loading">您已成功加入游戏等待主持人开始</div>
<div class="txt-loading2">游戏即将开始 敬请期待</div>
<!-- <div class="bg-bottom"></div> -->
......
......@@ -41,8 +41,8 @@ const CIRCLE_THROW_MS = 1200 // 圈圈飞行时长
const HIT_FADE_MS = 400 // 被套中后马消失延迟
// ── 难度递增参数:随游戏时间从 0→60s 指数曲线变化 ──
const HORSE_SPEED_MIN = 40 // px/s 初始速度
const HORSE_SPEED_MAX = 280 // px/s 最大速度(60s 时,7倍提升)
const HORSE_SPEED_MIN = 30 // px/s 初始速度
const HORSE_SPEED_MAX = 200 // px/s 最大速度(60s 时,7倍提升)
const SPAWN_MIN_START = 3000 // ms 初始最短生成间隔
const SPAWN_MIN_END = 800 // ms 最终最短生成间隔
const SPAWN_MAX_START = 6000 // ms 初始最长生成间隔
......@@ -103,12 +103,8 @@ let gameElapsed = 0 // 游戏已进行时间 ms(用于难度递增)
const HIT_EFFECT_MS = 800 // "+10" 动画持续时长
// ── 游戏状态 ──
const gameStarted = ref(false) // 3-2-1 倒计时完成标志,控制动画启停
const gameStarted = ref(true) // MobileStage 已完成 3-2-1,游戏直接开始
const gameActive = ref(true) // 游戏进行中(倒计时到 0 后变 false)
const preCountdown = ref(3) // 开场倒计时 3/2/1
const preCountdownKey = ref(0) // 强制动画重触发
const preCountdownVisible = ref(false) // 开场倒计时可见
let preCountdownTimer: ReturnType<typeof setTimeout> | undefined
// ── Web Audio API 音效 ──
let audioContext: AudioContext | null = null
......@@ -350,39 +346,6 @@ function preloadFrames() {
}
}
// ── 开场 3-2-1 倒计时 ──
function startPreGameCountdown() {
preCountdown.value = 3
preCountdownVisible.value = true
const tick = () => {
preCountdownKey.value++
if (preCountdown.value <= 1) {
preCountdownVisible.value = false
preCountdownTimer = undefined
// 倒计时结束 → 启动游戏
gameStarted.value = true
gameActive.value = true
prevTs = performance.now()
spawnHorse()
setTimeout(spawnHorse, 800)
setTimeout(spawnHorse, 1600)
raf = requestAnimationFrame(loop)
return
}
preCountdown.value--
preCountdownTimer = setTimeout(tick, 1000)
}
preCountdownTimer = setTimeout(tick, 1000)
}
function stopPreCountdown() {
if (preCountdownTimer) {
clearTimeout(preCountdownTimer)
preCountdownTimer = undefined
}
}
// ── 监听父组件倒计时,到 0 时暂停游戏 ──
watch(() => props.countdownInterval, (val) => {
if (val <= 0 && gameActive.value) {
......@@ -396,24 +359,22 @@ onMounted(() => {
preloadFrames()
// 先启动音频上下文(需用户首次交互后 resume,此处尝试提前激活)
try { getAudioContext() } catch { /* ignore */ }
// 开场 3-2-1 倒计时
startPreGameCountdown()
// MobileStage 已完成 3-2-1,游戏直接开始
prevTs = performance.now()
spawnHorse()
setTimeout(spawnHorse, 800)
setTimeout(spawnHorse, 1600)
raf = requestAnimationFrame(loop)
})
onBeforeUnmount(() => {
cancelAnimationFrame(raf)
stopPreCountdown()
disposeAudioContext()
})
</script>
<template>
<div class="game-stage">
<!-- 开场 3-2-1 倒计时遮罩 -->
<div v-if="preCountdownVisible" class="pre-countdown-overlay">
<div :key="preCountdownKey" class="pre-countdown-number">{{ preCountdown }}</div>
</div>
<Transition name="div-desc-slide" appear>
<div v-if="isDivDescVisible" class="div-desc">
<span class="desc-clock" aria-hidden="true"></span>
......@@ -492,38 +453,6 @@ onBeforeUnmount(() => {
padding: 0;
}
/* ── 开场 3-2-1 倒计时遮罩 ── */
/* 必须使用 MobileStage 的 viewport CSS 变量,才能覆盖缩放产生的侧边空白 */
.pre-countdown-overlay {
position: absolute;
left: var(--stage-viewport-left);
top: var(--stage-viewport-top);
width: var(--stage-viewport-width);
height: var(--stage-viewport-height);
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
pointer-events: none;
}
.pre-countdown-number {
color: #FFD700;
font-size: 200px;
font-weight: 900;
line-height: 1;
text-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
animation: countdown-pop 0.9s ease both;
}
@keyframes countdown-pop {
0% { opacity: 0; transform: scale(0.3); }
25% { opacity: 1; transform: scale(1.15); }
55% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.8); }
}
.div-desc {
position: absolute;
top: 150px;
......@@ -648,7 +577,7 @@ onBeforeUnmount(() => {
height: 100%;
background: v-bind('imageUrls.paodao') repeat-x;
background-size: auto 100%;
animation: runway-scroll 8s linear infinite;
animation: runway-scroll 12s linear infinite;
}
@keyframes runway-scroll {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
import game1MusicUrl from '@/assets/images/game1.mp3'
import game4MusicUrl from '@/assets/images/game1.mp3' // 需要改成正式的路径
import game5MusicUrl from '@/assets/images/game1.mp3' // 需要改成正式的路径
import game4MusicUrl from '@/assets/images/cmyf-bgm.mp3'
import game5MusicUrl from '@/assets/images/qcnf-bgm.mp3'
import game6MusicUrl from '@/assets/images/game6.mp3'
import go321 from '@/assets/images/321go.mp3'
import applause from '@/assets/images/applause.mp3'
......@@ -117,7 +117,7 @@ export function stopGame4Music() {
}
export async function playGame5Music() {
const audio = getBackgroundMusic(game4MusicUrl)
const audio = getBackgroundMusic(game5MusicUrl)
try {
await audio.play()
......
......@@ -111,7 +111,7 @@ const gotoRank1WithIntro = async () => {
screen.value = 'rank1'
await nextTick()
await rank1Ref.value?.startIntro()
screen.value = 'rank2'
// 结果页已在 Rank1View 内部渲染(showResult + result-overlay),不需要切到 rank2
isPlayingStartAnimation = false
}
......@@ -176,7 +176,7 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
screen.value = 'rank1'
break
case 3:
screen.value = 'rank2'
screen.value = 'rank1'
break
}
},
......@@ -437,6 +437,6 @@ watch(screen, (value) => {
<Loading v-if="screen === 'loading'" :players="loadingPlayers" :player-count="playerCount"
@start="startGameWithMusic" @back="backHandler" />
<Rank1 v-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" :horse-boosts="horseBoosts"
@start="startGameWithMusic" @next="nextRound" />
@start="startGameWithMusic" @next="nextRound" @back="backHandler" />
<!-- <Rank2 v-else-if="screen === 'rank2'" :rank-list="rankPlayers" @next="nextRound" /> -->
</template>
......@@ -222,5 +222,5 @@ watch(screen, (value) => {
<Rank1 v-else-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" @start="startGameWithMusic"
@next="nextRoundWithMusic" />
<!-- <Rank1 v-else ref="rank1Ref" :rank-list="rankPlayers" @start="startGameWithMusic" -->
<Rank2 v-else :rank-list="rankPlayers" @next="nextRoundWithMusic" />
<Rank2 v-else :rank-list="rankPlayers" @next="nextRoundWithMusic" @back="backHandler" />
</template>
......@@ -10,6 +10,7 @@ const props = defineProps<{
const emit = defineEmits<{
next: [];
back: [];
}>();
const FINAL_RANK_LIMIT = 10;
......@@ -30,7 +31,8 @@ const imageUrls = {
pai3: cssAssetUrl('game1/pai3.png'),
rankIcon: cssAssetUrl('game5/no-icon.webp'),
avatarAnimation: cssAssetUrl('avatar_animation.png'),
next: cssAssetUrl('game5/next-1.webp'),
next: cssAssetUrl('game1/next.png'),
back: cssAssetUrl('game1/back.png'),
};
const emptyPlayer = (index: number): Player => ({
......@@ -60,7 +62,7 @@ const renderAvatar = (item: any | null) => {
if (item && item.avatar) {
return `background: url('${item.avatar}') center / cover no-repeat;`
}
return `background: v-bind('imageUrls.avatar') center / cover no-repeat;`
return `background: ${imageUrls.avatar} center / cover no-repeat;`
}
</script>
......@@ -103,7 +105,7 @@ const renderAvatar = (item: any | null) => {
<div class="no-icon">{{ index + 4 }}</div>
<div>
<div class="img-avatar-container">
<div class="img-avatar"></div>
<div class="img-avatar" :style="`${renderAvatar(list[index + 3])}`"></div>
</div>
<div class="txt-nickname">{{ item.nickname }}</div>
</div>
......@@ -111,6 +113,7 @@ const renderAvatar = (item: any | null) => {
</div>
<div class="btn-next" @click="emit('next')"></div>
<div class="btn-back" @click="emit('back')"></div>
</div>
</DesignStage>
</template>
......@@ -155,10 +158,10 @@ const renderAvatar = (item: any | null) => {
.podium {
background: v-bind('imageUrls.podium') no-repeat center;
width: 1920px;
height: 800px;
height: 740px;
position: absolute;
bottom: 0px;
/* left: 287px; */
left: 0px;
.gu-burst {
width: 200px;
......@@ -180,16 +183,18 @@ const renderAvatar = (item: any | null) => {
.gu-burst {
left: 861px;
top: -72px;
z-index: 3;
}
.avatar {
/* background: v-bind('imageUrls.avatar') center / cover no-repeat; */
width: 120px;
height: 120px;
border-radius: 60px;
width: 160px;
height: 160px;
border-radius: 80px;
position: absolute;
left: 906px;
top: 76px;
left: 884px;
top: -7px;
z-index: 2;
}
.nickname {
......@@ -203,25 +208,19 @@ const renderAvatar = (item: any | null) => {
text-align: center;
line-height: 46px;
position: absolute;
left: 856px;
top: 190px;
z-index: 1;
left: 850px;
top: 160px;
z-index: 2;
border-radius: 23px;
}
.guan {
/* background: v-bind('imageUrls.guan1');
width: 64px;
height: 64px;
position: absolute;
left: 877px;
top: 61px; */
background: v-bind('imageUrls.guan');
width: 969px;
height: 238px;
position: absolute;
left: 482px;
top: -13px;
left: 480px;
top: -60px;
}
.pai {
......@@ -244,18 +243,17 @@ const renderAvatar = (item: any | null) => {
}
.avatar {
background: v-bind('imageUrls.avatar') center / cover no-repeat;
width: 144px;
height: 144px;
border-radius: 60px;
position: absolute;
left: 491.1px;
top: 74px;
left: 488px;
top: 27px;
}
.nickname {
text-align: center;
/* line-height: 46px; */
line-height: 52px;
width: 220px;
height: 52px;
background: linear-gradient(0deg, #AEA6A7 0%, #EAE4DF 100%);
......@@ -264,8 +262,8 @@ const renderAvatar = (item: any | null) => {
font-size: 30px;
color: #383636;
position: absolute;
left: 461px;
top: 221px;
left: 447px;
top: 184px;
z-index: 1;
border-radius: 23px;
}
......@@ -298,13 +296,12 @@ const renderAvatar = (item: any | null) => {
}
.avatar {
background: v-bind('imageUrls.avatar') center / cover no-repeat;
width: 144px;
height: 144px;
border-radius: 60px;
position: absolute;
left: 1300px;
top: 74px;
left: 1297px;
top: 27px;
}
.nickname {
......@@ -318,8 +315,8 @@ const renderAvatar = (item: any | null) => {
text-align: center;
line-height: 46px;
position: absolute;
left: 1240px;
top: 221px;
left: 1255px;
top: 160px;
z-index: 1;
border-radius: 23px;
}
......@@ -409,11 +406,20 @@ const renderAvatar = (item: any | null) => {
.btn-next {
position: absolute;
bottom: 100px;
left: 820px;
bottom: 50px;
left: 620px;
width: 316px;
height: 105px;
cursor: pointer;
background: v-bind('imageUrls.next') center / cover no-repeat;
width: 318px;
height: 95px;
}
.btn-back {
position: absolute;
bottom: 50px;
left: 1020px;
width: 316px;
height: 105px;
cursor: pointer;
background: v-bind('imageUrls.back') center / cover no-repeat;
}
</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