Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
SSIWB2504
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
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
template admin
SSIWB2504
Commits
e3bc91dc
Commit
e3bc91dc
authored
Aug 18, 2025
by
Tt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(form): 添加全局图片大小设置并优化鱼生成逻辑
parent
2b477b30
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
102 additions
and
27 deletions
+102
-27
form.component.html
src/app/form/form.component.html
+17
-10
form.component.ts
src/app/form/form.component.ts
+1
-1
interfaces.ts
src/app/play/interfaces.ts
+3
-0
play.component.ts
src/app/play/play.component.ts
+81
-16
No files found.
src/app/form/form.component.html
View file @
e3bc91dc
...
@@ -115,6 +115,23 @@
...
@@ -115,6 +115,23 @@
</nz-radio-group>
</nz-radio-group>
</div>
</div>
</div>
</div>
<!-- 图片大小设置区 -->
<div
class=
"border-dashed"
style=
"margin: 20px;width: 1000px;"
>
<div
class=
"word-input-title"
>
<span
style=
"font-size: 20px;"
>
图片大小设置:
</span>
</div>
<div
style=
"margin-top: 10px;"
>
<nz-radio-group
[(
ngModel
)]="
item
.
imgSize
"
(
ngModelChange
)="
ngChange
()"
>
<label
nz-radio
nzValue=
"big"
>
<span
[
style
]="{
color:item
.
imgSize=
='big'
?
'#
169BD5
'
:
'#
000
'}"
>
大图片(一屏3个图)
</span>
</label>
<label
nz-radio
nzValue=
"small"
>
<span
[
style
]="{
color:item
.
imgSize=
='small'
?
'#
169BD5
'
:
'#
000
'}"
>
小图片(一屏4个图)
</span>
</label>
</nz-radio-group>
</div>
</div>
</div>
</div>
<div
style=
"padding: 10px;background-color: #fff;display:none"
>
<div
style=
"padding: 10px;background-color: #fff;display:none"
>
<!-- 标题区 -->
<!-- 标题区 -->
...
@@ -154,16 +171,6 @@
...
@@ -154,16 +171,6 @@
</div>
</div>
<div>
<div>
<span
style=
"font-size: 20px;"
>
图片:
</span>
<span
style=
"font-size: 20px;"
>
图片:
</span>
<div
style=
"margin-top: 5px;"
>
<nz-radio-group
[(
ngModel
)]="
question
.
imgSize
"
(
ngModelChange
)="
ngChange
()"
>
<label
nz-radio
nzValue=
"big"
>
<span
[
style
]="{
color:question
.
imgSize=
='big'
?
'#
169BD5
'
:
'#
000
'}"
>
大图片(一屏3个图)
</span>
</label>
<label
nz-radio
nzValue=
"small"
>
<span
[
style
]="{
color:question
.
imgSize=
='small'
?
'#
169BD5
'
:
'#
000
'}"
>
小图片(一屏4个图)
</span>
</label>
</nz-radio-group>
</div>
<div
style=
"width:300px; margin-top: 10px;"
>
<div
style=
"width:300px; margin-top: 10px;"
>
<app-upload-image-with-preview
[
picUrl
]="
question
.
image
"
<app-upload-image-with-preview
[
picUrl
]="
question
.
image
"
(
imageUploaded
)="
onImageUploadSuccess
($
event
,'
image
',
question
)"
></app-upload-image-with-preview>
(
imageUploaded
)="
onImageUploadSuccess
($
event
,'
image
',
question
)"
></app-upload-image-with-preview>
...
...
src/app/form/form.component.ts
View file @
e3bc91dc
...
@@ -21,6 +21,7 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
...
@@ -21,6 +21,7 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
questionTextAudio
:
""
,
questionTextAudio
:
""
,
bgAudio
:
""
,
bgAudio
:
""
,
doubleModal
:
false
,
// 游戏模式:false为单人模式,true为双人模式
doubleModal
:
false
,
// 游戏模式:false为单人模式,true为双人模式
imgSize
:
'
big
'
,
// 统一设置图片大小
questions
:
[],
questions
:
[],
};
};
...
@@ -50,7 +51,6 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
...
@@ -50,7 +51,6 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
addQuestion
()
{
addQuestion
()
{
this
.
item
.
questions
.
push
({
this
.
item
.
questions
.
push
({
type
:
'
img
'
,
type
:
'
img
'
,
imgSize
:
'
big
'
,
// 默认大图片
audio
:
''
,
audio
:
''
,
image
:
''
,
image
:
''
,
time
:
null
,
time
:
null
,
...
...
src/app/play/interfaces.ts
View file @
e3bc91dc
...
@@ -43,6 +43,9 @@ export interface FormData {
...
@@ -43,6 +43,9 @@ export interface FormData {
bgAudio
:
string
;
bgAudio
:
string
;
/** 背景音频名称 */
/** 背景音频名称 */
audioBgName
?:
string
;
audioBgName
?:
string
;
/** 图片尺寸 */
imgSize
:
'
big
'
|
'
small
'
;
/** 问题列表 */
/** 问题列表 */
questions
:
Question
[];
questions
:
Question
[];
/** 类型 */
/** 类型 */
...
...
src/app/play/play.component.ts
View file @
e3bc91dc
...
@@ -53,6 +53,7 @@ let defaultFormData: FormData = {
...
@@ -53,6 +53,7 @@ let defaultFormData: FormData = {
"
doubleModal
"
:
false
,
"
doubleModal
"
:
false
,
"
questionText
"
:
""
,
"
questionType
"
:
"
read
"
,
"
questionTextAudio
"
:
""
,
"
questionText
"
:
""
,
"
questionType
"
:
"
read
"
,
"
questionTextAudio
"
:
""
,
"
bgAudio
"
:
""
,
"
bgAudio
"
:
""
,
imgSize
:
'
big
'
,
"
questions
"
:
[
"
questions
"
:
[
{
{
"
type
"
:
"
txt
"
,
"
audio
"
:
"
http://staging-teach.cdn.ireadabc.com/736c616e8b66a3ae03c8e8636ca46f0f.mp3
"
,
"
text
"
:
""
,
"
image
"
:
"
http://staging-teach.cdn.ireadabc.com/bde25b1b2a7fdf976f17ae80b6a20a25.png
"
,
"
time
"
:
null
,
"
duration
"
:
""
,
"
audioName
"
:
""
,
"
imgSize
"
:
"
big
"
"
type
"
:
"
txt
"
,
"
audio
"
:
"
http://staging-teach.cdn.ireadabc.com/736c616e8b66a3ae03c8e8636ca46f0f.mp3
"
,
"
text
"
:
""
,
"
image
"
:
"
http://staging-teach.cdn.ireadabc.com/bde25b1b2a7fdf976f17ae80b6a20a25.png
"
,
"
time
"
:
null
,
"
duration
"
:
""
,
"
audioName
"
:
""
,
"
imgSize
"
:
"
big
"
...
@@ -348,6 +349,7 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -348,6 +349,7 @@ export class PlayComponent implements OnInit, OnDestroy {
this
.
clearFishes
();
// 清空所有鱼元素
this
.
clearFishes
();
// 清空所有鱼元素
this
.
m_fishArray
=
[];
// 清空鱼群管理数组
this
.
m_fishArray
=
[];
// 清空鱼群管理数组
this
.
m_lastFishTime
=
0
;
// 重置上次生成鱼的时间
this
.
m_lastFishTime
=
0
;
// 重置上次生成鱼的时间
this
.
m_fishTimeAccumulator
=
0
;
// 重置鱼生成的累积时间
this
.
currentFishIndex
=
0
;
// 重置当前鱼索引
this
.
currentFishIndex
=
0
;
// 重置当前鱼索引
this
.
clickedFishData
=
[];
// 清空用户点击的鱼的数据
this
.
clickedFishData
=
[];
// 清空用户点击的鱼的数据
this
.
touchFish
=
null
;
// 清空当前触摸的鱼
this
.
touchFish
=
null
;
// 清空当前触摸的鱼
...
@@ -987,9 +989,26 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -987,9 +989,26 @@ export class PlayComponent implements OnInit, OnDestroy {
// 修改鱼的生成逻辑,使用当前阶段的随机问题列表
// 修改鱼的生成逻辑,使用当前阶段的随机问题列表
const
type
=
currentQuestion
.
questionRandomList
.
length
>
3
?
'
small
'
:
'
big
'
;
const
type
=
this
.
g_formData
.
imgSize
===
'
small
'
?
'
small
'
:
'
big
'
;
this
.
m_fishInterval
=
type
===
'
big
'
?
4700
:
3500
;
this
.
m_lastFishTime
=
0
;
// 重置上次生成鱼的时间,使下一帧立即生成鱼
// 计算鱼从右侧游到左侧所需的时间(毫秒)
// 假设鱼的速度为1像素/帧,帧率约为60fps,则速度约为60像素/秒
// 屏幕宽度加上鱼的宽度,再除以速度,得到鱼游过整个屏幕的时间
const
fishSpeed
=
1
;
// 鱼的移动速度
const
fishWidth
=
374
*
this
.
g_mapScale
*
(
type
===
'
small
'
?
0.7
:
1
);
// 估计的鱼宽度
const
screenWidth
=
this
.
g_canvasWidth
+
fishWidth
*
0
;
// 屏幕宽度加上鱼的宽度
const
fishCrossingTime
=
(
screenWidth
/
fishSpeed
)
*
(
1000
/
60
);
// 鱼游过屏幕所需的时间(毫秒)
// 根据imgSize类型确定最大鱼数量
const
maxFishCount
=
type
===
'
small
'
?
4
:
3
;
// 计算合适的鱼生成间隔时间
// 鱼游过屏幕的时间除以最大鱼数量,确保屏幕上最多同时有maxFishCount条鱼
this
.
m_fishInterval
=
Math
.
floor
(
fishCrossingTime
/
maxFishCount
);
console
.
log
(
`鱼类型:
${
type
}
, 最大鱼数:
${
maxFishCount
}
, 鱼生成间隔:
${
this
.
m_fishInterval
}
ms`
);
this
.
m_fishTimeAccumulator
=
this
.
m_fishInterval
;
// 重置累积时间,使下一帧立即生成鱼
// 每一个stage开始的时候播放一次正确问题的音频
// 每一个stage开始的时候播放一次正确问题的音频
if
(
currentQuestion
.
correctQuestion
&&
currentQuestion
.
correctQuestion
.
audio
)
{
if
(
currentQuestion
.
correctQuestion
&&
currentQuestion
.
correctQuestion
.
audio
)
{
...
@@ -1213,6 +1232,7 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -1213,6 +1232,7 @@ export class PlayComponent implements OnInit, OnDestroy {
}
}
createFish
(
type
,
data
)
{
createFish
(
type
,
data
)
{
console
.
log
(
'
createFish
'
,
type
)
const
mapScale
=
this
.
g_mapScale
>
1
?
1
:
this
.
g_mapScale
;
const
mapScale
=
this
.
g_mapScale
>
1
?
1
:
this
.
g_mapScale
;
// 创建鱼元素
// 创建鱼元素
const
fishId
=
`fish_
${
Math
.
floor
(
Math
.
random
()
*
1000
)}
_
${
Date
.
now
()}
`
const
fishId
=
`fish_
${
Math
.
floor
(
Math
.
random
()
*
1000
)}
_
${
Date
.
now
()}
`
...
@@ -2810,14 +2830,45 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2810,14 +2830,45 @@ export class PlayComponent implements OnInit, OnDestroy {
private
m_lastFishTime
=
0
;
private
m_lastFishTime
=
0
;
private
m_fishInterval
=
3000
;
// 每隔10秒生成一条新鱼
private
m_fishInterval
=
3000
;
// 每隔10秒生成一条新鱼
// 添加帧率控制相关属性
private
m_targetFPS
=
60
;
// 目标帧率:60帧/秒
private
m_frameInterval
=
1000
/
60
;
// 每帧间隔时间(毫秒)
private
m_lastFrameTime
=
0
;
// 上一帧的时间戳
/**
/**
* 更新游戏画面
* 更新游戏画面
* 负责清除画布、更新动画和渲染所有元素
* 负责清除画布、更新动画和渲染所有元素
* 控制帧率为60帧/秒,避免因显示频率144Hz导致刷新过快
*/
*/
update
()
{
update
()
{
this
.
g_animationId
&&
window
.
cancelAnimationFrame
(
this
.
g_animationId
);
this
.
g_animationId
&&
window
.
cancelAnimationFrame
(
this
.
g_animationId
);
// 请求下一帧动画,形成动画循环
// 请求下一帧动画,形成动画循环
this
.
g_animationId
=
window
.
requestAnimationFrame
(
this
.
update
.
bind
(
this
));
this
.
g_animationId
=
window
.
requestAnimationFrame
(
this
.
update
.
bind
(
this
));
// 帧率控制:计算当前时间与上一帧时间的差值
const
currentTime
=
Date
.
now
();
const
elapsed
=
currentTime
-
this
.
m_lastFrameTime
;
// 计算时间增量(秒)
const
dt
=
elapsed
/
1000
;
// 如果经过的时间小于帧间隔,则跳过本次更新
if
(
elapsed
<
this
.
m_frameInterval
)
{
return
;
}
// 如果时间间隔过大(大于0.1秒),说明可能是后台运行或性能问题,限制最大时间增量
// 避免因为大的时间间隔导致渲染异常
if
(
dt
>
0.1
)
{
console
.
log
(
'
时间间隔过大,限制更新:
'
,
dt
,
'
秒
'
);
// 更新时间戳但不执行渲染
this
.
m_lastFrameTime
=
currentTime
;
return
;
}
// 更新上一帧时间(考虑实际执行时间的偏差)
this
.
m_lastFrameTime
=
currentTime
-
(
elapsed
%
this
.
m_frameInterval
);
// 清除画布内容,准备绘制新的一帧
// 清除画布内容,准备绘制新的一帧
this
.
g_ctx
.
clearRect
(
0
,
0
,
this
.
g_canvasWidth
,
this
.
g_canvasHeight
);
this
.
g_ctx
.
clearRect
(
0
,
0
,
this
.
g_canvasWidth
,
this
.
g_canvasHeight
);
// 更新TWEEN动画系统
// 更新TWEEN动画系统
...
@@ -2828,8 +2879,8 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2828,8 +2879,8 @@ export class PlayComponent implements OnInit, OnDestroy {
this
.
updateArr
(
this
.
m_endPageArr
);
this
.
updateArr
(
this
.
m_endPageArr
);
// 更新元素花瓣效果数组中的所有元素
// 更新元素花瓣效果数组中的所有元素
this
.
updateArr
(
this
.
m_elementPetalArr
);
this
.
updateArr
(
this
.
m_elementPetalArr
);
// 更新鱼的运动
// 更新鱼的运动
,传入时间增量dt
this
.
updateFish
();
this
.
updateFish
(
dt
);
// 如果游戏已开始,更新时间显示
// 如果游戏已开始,更新时间显示
if
(
this
.
isGameStart
&&
this
.
g_formData
.
doubleModal
)
{
if
(
this
.
isGameStart
&&
this
.
g_formData
.
doubleModal
)
{
...
@@ -2880,11 +2931,14 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2880,11 +2931,14 @@ export class PlayComponent implements OnInit, OnDestroy {
/**
/**
* 更新鱼的运动
* 更新鱼的运动
* 管理鱼从右侧游到左侧的动画效果
* 管理鱼从右侧游到左侧的动画效果
* @param dt 时间增量(秒),用于计算鱼的移动距离和生成间隔
*/
*/
private
isGameStart
=
false
;
private
isGameStart
=
false
;
private
currentFishIndex
=
0
;
private
currentFishIndex
=
0
;
updateFish
()
{
private
m_fishTimeAccumulator
=
0
;
// 累积的时间,用于鱼的生成
updateFish
(
dt
:
number
=
0
)
{
// 如果没有游戏阶段或当前阶段索引超出范围,不生成鱼
// 如果没有游戏阶段或当前阶段索引超出范围,不生成鱼
if
(
!
this
.
gameStages
||
this
.
currentStageIndex
>=
this
.
gameStages
.
length
)
{
if
(
!
this
.
gameStages
||
this
.
currentStageIndex
>=
this
.
gameStages
.
length
)
{
return
;
return
;
...
@@ -2894,11 +2948,12 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2894,11 +2948,12 @@ export class PlayComponent implements OnInit, OnDestroy {
// 获取当前游戏阶段
// 获取当前游戏阶段
const
currentStage
=
this
.
gameStages
[
this
.
currentStageIndex
];
const
currentStage
=
this
.
gameStages
[
this
.
currentStageIndex
];
// 检查是否需要创建新的鱼
// 累积时间,用于鱼的生成
const
currentTime
=
Date
.
now
();
this
.
m_fishTimeAccumulator
+=
dt
*
1000
;
// 转换为毫秒
const
mapScale
=
this
.
g_mapScale
<
1
?
1
:
this
.
g_mapScale
;
if
(
currentTime
-
this
.
m_lastFishTime
>
this
.
m_fishInterval
)
{
// 使用累积的时间来控制鱼的生成频率,从而间接控制屏幕上的鱼的数量
// 创建新的鱼
if
(
this
.
m_fishTimeAccumulator
>=
this
.
m_fishInterval
)
{
// 创建新的鱼
if
(
currentStage
&&
currentStage
.
questionRandomList
&&
currentStage
.
questionRandomList
.
length
>
0
)
{
if
(
currentStage
&&
currentStage
.
questionRandomList
&&
currentStage
.
questionRandomList
.
length
>
0
)
{
// 从当前阶段的随机问题列表中选择一个问题数据
// 从当前阶段的随机问题列表中选择一个问题数据
if
(
!
this
.
currentFishIndex
)
this
.
currentFishIndex
=
0
;
if
(
!
this
.
currentFishIndex
)
this
.
currentFishIndex
=
0
;
...
@@ -2906,7 +2961,8 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2906,7 +2961,8 @@ export class PlayComponent implements OnInit, OnDestroy {
const
fishData
=
currentStage
.
questionRandomList
[
randomIndex
];
const
fishData
=
currentStage
.
questionRandomList
[
randomIndex
];
this
.
currentFishIndex
++
;
this
.
currentFishIndex
++
;
const
type
=
currentStage
.
questionRandomList
.
length
>
3
?
'
small
'
:
'
big
'
;
const
type
=
this
.
g_formData
.
imgSize
==
'
small
'
?
'
small
'
:
'
big
'
;
// 创建小型鱼
// 创建小型鱼
const
fish
=
this
.
createFish
(
type
,
fishData
);
const
fish
=
this
.
createFish
(
type
,
fishData
);
...
@@ -2918,7 +2974,7 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2918,7 +2974,7 @@ export class PlayComponent implements OnInit, OnDestroy {
fish
.
ref
.
y
=
(
this
.
currentFishIndex
%
2
==
0
?
400
:
300
)
*
this
.
g_mapScale
;
// Math.random() * (this.g_canvasHeight * 0.35) + this.g_canvasHeight * 0.25; // 在屏幕中间区域随机高度
fish
.
ref
.
y
=
(
this
.
currentFishIndex
%
2
==
0
?
400
:
300
)
*
this
.
g_mapScale
;
// Math.random() * (this.g_canvasHeight * 0.35) + this.g_canvasHeight * 0.25; // 在屏幕中间区域随机高度
// 设置鱼的游动速度
// 设置鱼的游动速度
fish
.
speed
=
0.45
*
2
*
this
.
g_mapScale
;
fish
.
speed
=
1
;
// 确保鱼已添加到渲染队列中
// 确保鱼已添加到渲染队列中
// createFish 方法中已经调用了 render 方法,但为了确保,这里再次检查
// createFish 方法中已经调用了 render 方法,但为了确保,这里再次检查
...
@@ -2930,8 +2986,8 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2930,8 +2986,8 @@ export class PlayComponent implements OnInit, OnDestroy {
// 将鱼添加到管理数组
// 将鱼添加到管理数组
this
.
m_fishArray
.
push
(
fish
);
this
.
m_fishArray
.
push
(
fish
);
//
更新最后创建鱼的时间
//
重置累积时间(减去间隔时间,保留余数,避免时间损失)
this
.
m_
lastFishTime
=
currentTime
;
this
.
m_
fishTimeAccumulator
-=
this
.
m_fishInterval
;
}
}
}
}
...
@@ -2939,8 +2995,13 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -2939,8 +2995,13 @@ export class PlayComponent implements OnInit, OnDestroy {
for
(
let
i
=
this
.
m_fishArray
.
length
-
1
;
i
>=
0
;
i
--
)
{
for
(
let
i
=
this
.
m_fishArray
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
fish
=
this
.
m_fishArray
[
i
];
const
fish
=
this
.
m_fishArray
[
i
];
// 基于时间增量dt计算鱼的移动距离,确保在不同帧率下移动速度一致
// 鱼的基础速度是每秒60像素(1像素/帧 * 60帧/秒)
const
baseSpeed
=
60
;
// 每秒移动的像素数
const
moveDistance
=
baseSpeed
*
dt
*
fish
.
speed
;
// 向左移动鱼
// 向左移动鱼
fish
.
ref
.
x
-=
fish
.
speed
;
fish
.
ref
.
x
-=
moveDistance
;
// 如果鱼游出了屏幕左侧,从数组中移除
// 如果鱼游出了屏幕左侧,从数组中移除
if
(
fish
.
ref
.
x
<
-
fish
.
ref
.
width
*
fish
.
ref
.
scaleX
/
2
)
{
if
(
fish
.
ref
.
x
<
-
fish
.
ref
.
width
*
fish
.
ref
.
scaleX
/
2
)
{
...
@@ -3343,6 +3404,10 @@ export class PlayComponent implements OnInit, OnDestroy {
...
@@ -3343,6 +3404,10 @@ export class PlayComponent implements OnInit, OnDestroy {
// 初始化舞台
// 初始化舞台
this
.
initStage
();
this
.
initStage
();
// 初始化帧率控制的时间戳
this
.
m_lastFrameTime
=
Date
.
now
();
// 更新画面渲染
// 更新画面渲染
this
.
update
();
this
.
update
();
}
}
...
...
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