Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
fc-minigame
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈冲
fc-minigame
Commits
cf46d9ce
Commit
cf46d9ce
authored
Jun 09, 2026
by
董政锦
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: game4 和 game5 完成;
parent
b64c8408
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
61 additions
and
105 deletions
+61
-105
PlayingView.vue
frontend-h5/src/pages/game4/views/PlayingView.vue
+1
-3
Rank1View.vue
frontend-large/src/pages/game4/views/Rank1View.vue
+24
-17
Rank1View.vue
frontend-large/src/pages/game5/views/Rank1View.vue
+36
-85
No files found.
frontend-h5/src/pages/game4/views/PlayingView.vue
View file @
cf46d9ce
...
...
@@ -176,14 +176,12 @@ onBeforeUnmount(() => {
}
})
// 点击马触发:iOS 先请求权限
+ 触发射分
// 点击马触发:iOS 先请求权限
(不触发得分)
const
onTapHorse
=
async
()
=>
{
if
(
!
motionPermissionGranted
)
{
await
requestMotionPermission
()
// 权限请求完成(无论结果)隐藏 iOS 提示
showIOSHint
.
value
=
false
}
emit
(
'touch'
)
}
</
script
>
...
...
frontend-large/src/pages/game4/views/Rank1View.vue
View file @
cf46d9ce
...
...
@@ -245,7 +245,7 @@ const startGameLoop = () => {
return
;
}
if
(
!
gameLastTs
)
gameLastTs
=
ts
;
const
dt
=
Math
.
min
(
ts
-
gameLastTs
,
5
0
);
const
dt
=
Math
.
min
(
ts
-
gameLastTs
,
20
0
);
gameLastTs
=
ts
;
const
newTransientBoosts
:
Record
<
string
,
number
>
=
{};
...
...
@@ -263,7 +263,7 @@ const startGameLoop = () => {
);
horse
.
frameTimer
+=
dt
;
if
(
horse
.
frameTimer
>=
HORSE_FRAME_TICK_MS
)
{
while
(
horse
.
frameTimer
>=
HORSE_FRAME_TICK_MS
)
{
horse
.
frameTimer
-=
HORSE_FRAME_TICK_MS
;
horse
.
frameIndex
=
(
horse
.
frameIndex
+
1
)
%
HORSE_FRAME_COUNT
;
}
...
...
@@ -474,7 +474,9 @@ defineExpose({
transform: `translateX(${horseTranslateX(horse.xPercent)}px)`,
}"
>
<div
class=
"horse"
:style=
"horseSpriteStyle(horse)"
></div>
<div
class=
"horse"
:style=
"horseSpriteStyle(horse)"
>
<div
class=
"horse-shadow"
></div>
</div>
<div
class=
"horse-username"
>
<span
class=
"username-text"
>
{{
horse
.
nickname
}}
</span>
</div>
...
...
@@ -619,10 +621,21 @@ defineExpose({
.horse
{
width
:
120px
;
height
:
109px
;
background
:
url("@/assets/images/game4/horse.png")
-17px
0
/
1472px
109px
no-repeat
;
animation
:
horse-run
0.6s
steps
(
12
)
infinite
;
filter
:
drop-shadow
(
4px
6px
6px
rgba
(
0
,
0
,
0
,
0.5
));
background-repeat
:
no-repeat
;
position
:
relative
;
}
.horse-shadow
{
position
:
absolute
;
bottom
:
-2px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
width
:
120px
;
height
:
18px
;
background
:
radial-gradient
(
ellipse
,
rgba
(
0
,
0
,
0
,
0.45
)
0%
,
transparent
70%
);
border-radius
:
50%
;
pointer-events
:
none
;
z-index
:
0
;
}
.horse-username
{
...
...
@@ -633,7 +646,7 @@ defineExpose({
height
:
40px
;
position
:
absolute
;
top
:
10px
;
left
:
-2
4
px
;
left
:
-2
2
px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
...
...
@@ -649,6 +662,8 @@ defineExpose({
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
padding-left
:
4px
;
box-sizing
:
border-box
;
}
.horse-avatar
{
...
...
@@ -658,7 +673,7 @@ defineExpose({
height
:
30px
;
position
:
absolute
;
top
:
-4px
;
left
:
-
56
px
;
left
:
-
22
px
;
border-radius
:
50%
;
}
...
...
@@ -671,14 +686,6 @@ defineExpose({
}
}
@keyframes
horse-run
{
0
%
{
background-position
:
-17px
0
;
}
100
%
{
background-position
:
-1457px
0
;
}
}
/* ========== 结果弹窗 ========== */
.result-overlay
{
...
...
frontend-large/src/pages/game5/views/Rank1View.vue
View file @
cf46d9ce
...
...
@@ -39,10 +39,7 @@ const HORSE_FRAME_COUNT = 21; // 每匹马 21 帧(00-20)
const
HORSE_WIDTH
=
200
;
const
HORSE_START_X
=
-
330
;
// 左侧屏外起点
const
HORSE_END_X
=
1920
-
HORSE_WIDTH
;
// 右边缘贴齐屏幕右侧(1720px)
const
HORSE_RUN_DURATION_SEC
=
60
;
const
horseRunDuration
=
`
${
HORSE_RUN_DURATION_SEC
}
s`
;
const
horseStartX
=
`
${
HORSE_START_X
}
px`
;
const
horseEndX
=
`
${
HORSE_END_X
}
px`
;
// 预构建四种马的帧 URL(与 H5 PlayingView 一致,避免运行时拼路径)
const
horseFrames
:
Record
<
number
,
string
[]
>
=
{};
...
...
@@ -54,11 +51,13 @@ for (let h = 1; h <= 4; h++) {
interface
Horse
{
id
:
string
;
playerId
:
string
;
// 对应玩家的唯一标识
horseType
:
number
;
// 1-4 颜色
lane
:
"top"
|
"bottom"
;
frameIndex
:
number
;
// 当前帧 0-20
frameTimer
:
number
;
// 帧切换计时器
startTime
:
number
;
// 开始移动的时间戳(ms)
durationMs
:
number
;
// 跑完全程时长(ms)
x
:
number
;
// 当前 X 位置
}
const
horses
=
ref
<
Horse
[]
>
([]);
...
...
@@ -112,10 +111,7 @@ watch(
if
(
rankList
&&
rankList
.
length
>
0
)
{
console
.
log
(
'[Rank1View] 排名列表更新 - 数据:'
,
JSON
.
stringify
(
rankList
.
map
(
p
=>
({
nickname
:
p
.
nickname
,
score
:
p
.
score
}))))
updateRankList
(
rankList
);
// 游戏进行中时,同步马匹数量与玩家数量
if
(
isGameRunning
.
value
)
{
syncHorsesWithPlayers
();
}
}
},
{
immediate
:
true
,
deep
:
true
},
...
...
@@ -141,7 +137,7 @@ const startIntro = async () => {
// Phase 2: 倒计时结束 → 游戏正式开始,背景动画+马匹启动
gameStarted
.
value
=
true
;
isGameRunning
.
value
=
true
;
syncHorsesWithPlayers
();
// 根据当前玩家数量初始化马匹
generateRandomHorses
();
startHorseLoop
();
// 等待 60s 游戏倒计时完全结束
...
...
@@ -161,12 +157,6 @@ let horseRafId: number | undefined;
let
horseLastTs
=
0
;
const
HORSE_FRAME_TICK_MS
=
48
;
// 每帧间隔 ~20fps
const
onHorseAnimationEnd
=
(
horseId
:
string
)
=>
{
const
idx
=
horses
.
value
.
findIndex
((
h
)
=>
h
.
id
===
horseId
);
if
(
idx
>=
0
)
{
horses
.
value
.
splice
(
idx
,
1
);
}
};
const
startHorseLoop
=
()
=>
{
stopHorseLoop
();
...
...
@@ -178,12 +168,18 @@ const startHorseLoop = () => {
return
;
}
if
(
!
horseLastTs
)
horseLastTs
=
ts
;
const
dt
=
Math
.
min
(
ts
-
horseLastTs
,
5
0
);
const
dt
=
Math
.
min
(
ts
-
horseLastTs
,
20
0
);
horseLastTs
=
ts
;
for
(
const
horse
of
horses
.
value
)
{
if
(
ts
<
horse
.
startTime
)
continue
;
const
elapsed
=
ts
-
horse
.
startTime
;
const
progress
=
Math
.
min
(
elapsed
/
horse
.
durationMs
,
1
);
horse
.
x
=
HORSE_START_X
+
(
HORSE_END_X
-
HORSE_START_X
)
*
progress
;
horse
.
frameTimer
+=
dt
;
if
(
horse
.
frameTimer
>=
HORSE_FRAME_TICK_MS
)
{
while
(
horse
.
frameTimer
>=
HORSE_FRAME_TICK_MS
)
{
horse
.
frameTimer
-=
HORSE_FRAME_TICK_MS
;
horse
.
frameIndex
=
(
horse
.
frameIndex
+
1
)
%
HORSE_FRAME_COUNT
;
}
...
...
@@ -201,65 +197,33 @@ const stopHorseLoop = () => {
}
};
// ========== 根据玩家数量生成马匹 ==========
/** 获取有效玩家(排除虚位以待) */
const
getValidPlayers
=
()
=>
{
return
props
.
rankList
.
filter
(
(
p
)
=>
p
.
nickname
!==
"虚位以待"
&&
!
p
.
wechat_id
?.
startsWith
(
"empty-"
)
&&
!
p
.
userid
?.
startsWith
(
"empty-"
),
);
};
// ========== 自动随机生成 8-10 匹马 ==========
let
gameStartTime
=
0
;
/** 创建一匹马 */
const
createHorse
=
(
playerId
:
string
,
lane
:
"top"
|
"bottom"
)
=>
{
const
type
=
HORSE_TYPES
[
Math
.
floor
(
Math
.
random
()
*
HORSE_TYPES
.
length
)]
!
;
return
{
const
generateRandomHorses
=
()
=>
{
const
count
=
8
+
Math
.
floor
(
Math
.
random
()
*
3
);
// 8-10
const
lanes
:
(
"top"
|
"bottom"
)[]
=
[
"top"
,
"bottom"
];
const
newHorses
:
Horse
[]
=
[];
const
now
=
performance
.
now
();
for
(
let
i
=
0
;
i
<
count
;
i
++
)
{
const
type
=
HORSE_TYPES
[
i
%
HORSE_TYPES
.
length
]
!
;
const
lane
=
lanes
[
i
%
2
]
!
;
const
delayMs
=
i
*
2500
+
Math
.
random
()
*
1000
;
// 每匹马间隔2.5秒起步,加随机1秒
const
durationMs
=
(
40
+
Math
.
random
()
*
40
)
*
1000
;
// 40-80秒,速度浮动更大
newHorses
.
push
({
id
:
`horse-
${
++
horseIdCounter
}
`
,
playerId
,
horseType
:
type
,
lane
,
frameIndex
:
Math
.
floor
(
Math
.
random
()
*
HORSE_FRAME_COUNT
),
frameTimer
:
0
,
};
};
/** 根据玩家数量同步生成马匹 */
const
syncHorsesWithPlayers
=
()
=>
{
const
validPlayers
=
getValidPlayers
();
const
currentHorseCount
=
horses
.
value
.
length
;
if
(
validPlayers
.
length
===
0
)
return
;
if
(
currentHorseCount
<
validPlayers
.
length
)
{
// 需要增加马匹
const
lanes
:
(
"top"
|
"bottom"
)[]
=
[
"top"
,
"bottom"
];
const
needed
=
validPlayers
.
length
-
currentHorseCount
;
const
newHorses
:
Horse
[]
=
[];
for
(
let
i
=
0
;
i
<
needed
;
i
++
)
{
const
playerIndex
=
currentHorseCount
+
i
;
const
player
=
validPlayers
[
playerIndex
];
if
(
!
player
)
break
;
const
playerId
=
player
.
userid
||
player
.
wechat_id
||
player
.
avatar
||
`player-
${
playerIndex
}
`
;
const
lane
=
lanes
[
newHorses
.
length
%
2
]
??
"top"
;
newHorses
.
push
(
createHorse
(
playerId
,
lane
));
}
if
(
newHorses
.
length
>
0
)
{
horses
.
value
=
[...
horses
.
value
,
...
newHorses
];
}
}
else
if
(
currentHorseCount
>
validPlayers
.
length
)
{
// 需要减少马匹(移除多出的)
// 保留对应玩家的马
const
filtered
=
horses
.
value
.
filter
((
h
)
=>
{
const
player
=
validPlayers
.
find
(
(
p
)
=>
(
p
.
userid
||
p
.
wechat_id
||
p
.
avatar
)
===
h
.
playerId
,
);
return
player
!==
undefined
;
startTime
:
now
+
delayMs
,
durationMs
,
x
:
HORSE_START_X
,
});
// 如果 filtered 数量仍多于玩家,取前 N 个
horses
.
value
=
filtered
.
slice
(
0
,
validPlayers
.
length
);
}
horses
.
value
=
newHorses
;
};
// ========== 预加载所有马匹帧图片(防止切帧时闪烁/白屏)==========
...
...
@@ -277,7 +241,7 @@ onMounted(() => {
if
(
$is_run_local
())
{
gameStarted
.
value
=
true
;
isGameRunning
.
value
=
true
;
syncHorsesWithPlayers
();
// 本地调试模式下也同步马匹
generateRandomHorses
();
startHorseLoop
();
}
});
...
...
@@ -364,9 +328,9 @@ const rankIcon = (index: number) => {
<div
v-for=
"horse in horses"
:key=
"horse.id"
class=
"horse-wrapper
horse-running
"
class=
"horse-wrapper"
:class=
"`horse-lane-$
{horse.lane}`"
@animationend="onHorseAnimationEnd(horse.id)
"
:style="{ transform: `translateX(${horse.x}px)` }
"
>
<div
class=
"horse-sprite"
>
<img
...
...
@@ -567,19 +531,6 @@ const rankIcon = (index: number) => {
bottom
:
60px
;
}
/* 水平位移:60s 从左侧屏外跑到右边缘贴齐屏幕,避免提前跑出画面 */
.horse-running
{
animation
:
horse-run
v-bind
(
horseRunDuration
)
linear
forwards
;
}
@keyframes
horse-run
{
from
{
transform
:
translateX
(
v-bind
(
horseStartX
));
}
to
{
transform
:
translateX
(
v-bind
(
horseEndX
));
}
}
.horse-sprite
{
position
:
relative
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment