Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
paopao
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
cocktail-party
paopao
Commits
7b6d9a2c
提交
7b6d9a2c
authored
3月 23, 2026
作者:
lidongxu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
添加王小卤元素完成
上级
f2fef882
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
86 行增加
和
69 行删除
+86
-69
bubble.png
big-screen/public/images/bubble.png
+0
-0
bubble1.png
big-screen/public/images/bubble1.png
+0
-0
chicken.png
big-screen/public/images/chicken.png
+0
-0
bubble.js
big-screen/src/renderer/bubble.js
+21
-21
shooter.js
big-screen/src/renderer/shooter.js
+65
-48
没有找到文件。
big-screen/public/images/bubble.png
查看替换文件 @
f2fef882
浏览文件 @
7b6d9a2c
346.8 KB
|
W:
|
H:
463.6 KB
|
W:
|
H:
2-up
Swipe
Onion skin
big-screen/public/images/bubble1.png
0 → 100644
浏览文件 @
7b6d9a2c
346.8 KB
big-screen/public/images/chicken.png
0 → 100644
浏览文件 @
7b6d9a2c
25.4 KB
big-screen/src/renderer/bubble.js
浏览文件 @
7b6d9a2c
...
...
@@ -9,37 +9,37 @@ export const BUBBLE_RADIUS = SCREEN_WIDTH / 22
const
ROW_HEIGHT
=
BUBBLE_RADIUS
*
Math
.
sqrt
(
3
)
/**
* 9 种泡泡颜色(颜色索引 1-9),用于爆炸粒子效果取色
* 9 种泡泡颜色(颜色索引 1-9),用于爆炸粒子效果取色。
* 排序与小程序端一致:按与鸡爪对比度从高到低排列。
*/
export
const
BUBBLE_COLORS
=
[
''
,
'#
E83030'
,
'#1DB85A'
,
'#
2BC8E8'
,
'#
E8C000'
,
'#
F07820'
,
'#8
B35E0'
,
'#
E060A0'
,
'#
D8D0B0'
,
'#
80C020'
,
'#
2BC8E8'
,
// 1 蓝
'#1DB85A'
,
// 2 绿
'#
E8C000'
,
// 3 黄
'#
8B35E0'
,
// 4 紫
'#
E060A0'
,
// 5 粉
'#8
0C020'
,
// 6 黄绿
'#
D8D0B0'
,
// 7 奶白
'#
F07820'
,
// 8 橙
'#
E83030'
,
// 9 红
]
/**
* 精灵图 bubble.png(1400×1400)中每个球的裁剪区域
* 3×3 排列,从左上按行序编号 1-9
* 字段:[sx, sy, sw, sh]
* 索引对应 colorIdx,映射到精灵图中正确的球(与小程序端一致)
*/
const
SPRITE_REGIONS
=
[
null
,
[
1
59
,
158
,
296
,
297
],
[
581
,
562
,
297
,
296
],
[
1004
,
562
,
297
,
296
],
[
1
004
,
158
,
297
,
297
],
[
581
,
158
,
297
,
297
],
[
159
,
965
,
296
,
297
],
[
581
,
965
,
297
,
297
],
[
1004
,
965
,
297
,
297
],
[
159
,
562
,
296
,
296
],
[
1
004
,
562
,
297
,
296
],
// 1 蓝 ← 精灵图位置6(右中)
[
581
,
562
,
297
,
296
],
// 2 绿 ← 精灵图位置5(中中)
[
1004
,
158
,
297
,
297
],
// 3 黄 ← 精灵图位置3(右上)
[
1
59
,
965
,
296
,
297
],
// 4 紫 ← 精灵图位置7(左下)
[
581
,
965
,
297
,
297
],
// 5 粉 ← 精灵图位置8(中下)
[
159
,
562
,
296
,
296
],
// 6 黄绿 ← 精灵图位置4(左中)
[
1004
,
965
,
297
,
297
],
// 7 奶白 ← 精灵图位置9(右下)
[
581
,
158
,
297
,
297
],
// 8 橙 ← 精灵图位置2(中上)
[
159
,
158
,
296
,
297
],
// 9 红 ← 精灵图位置1(左上)
]
// 浏览器端:使用 new Image() 加载精灵图
...
...
big-screen/src/renderer/shooter.js
浏览文件 @
7b6d9a2c
/**
* 射击器渲染(
移植自 minigame-1
,仅绘制不含触摸)
*
发射器底座、瞄准线、当前泡泡、下一颗预览泡泡
* 射击器渲染(
大屏端
,仅绘制不含触摸)
*
鸡身体朝向跟随瞄准方向倾斜 + 呼吸浮动 + 弹跳动画
*/
import
{
SCREEN_WIDTH
,
SCREEN_HEIGHT
,
SAFE_AREA_BOTTOM
}
from
'../constants.js'
import
{
BUBBLE_RADIUS
,
drawBubble3D
}
from
'./bubble.js'
const
CURRENT_Y_OFFSET
=
BUBBLE_RADIUS
*
3.5
// 鸡的尺寸
const
CHICKEN_WIDTH
=
BUBBLE_RADIUS
*
5
const
CHICKEN_HEIGHT
=
BUBBLE_RADIUS
*
6
const
CHICKEN_BOTTOM_MARGIN
=
BUBBLE_RADIUS
*
0.8
const
BUBBLE_TOP_OFFSET
=
BUBBLE_RADIUS
*
0.4
const
NEXT_X_OFFSET
=
BUBBLE_RADIUS
*
3.2
const
NEXT_Y_OFFSET
=
BUBBLE_RADIUS
*
0.5
const
NEXT_SCALE
=
0.65
/**
* 根据发射角度计算瞄准线折线点(含左右墙壁反弹,直到超出顶部)
*/
// 鸡朝向参数
const
MIN_ANGLE
=
15
*
Math
.
PI
/
180
const
MAX_ANGLE
=
165
*
Math
.
PI
/
180
const
MAX_LEAN_ANGLE
=
25
*
Math
.
PI
/
180
const
LEAN_LERP
=
0.15
const
BOB_SPEED
=
0.03
const
BOB_AMOUNT
=
BUBBLE_RADIUS
*
0.15
// 加载鸡图片
const
_chickenImg
=
new
Image
()
_chickenImg
.
src
=
'/images/chicken.png'
// 模块级动画状态
let
_currentLean
=
0
let
_bobPhase
=
0
function
calcAimPoints
(
originX
,
originY
,
angle
)
{
const
R
=
BUBBLE_RADIUS
const
points
=
[{
x
:
originX
,
y
:
originY
}]
let
cx
=
originX
let
cy
=
originY
let
vx
=
Math
.
cos
(
angle
)
let
vy
=
-
Math
.
sin
(
angle
)
let
cx
=
originX
,
cy
=
originY
let
vx
=
Math
.
cos
(
angle
),
vy
=
-
Math
.
sin
(
angle
)
for
(
let
i
=
0
;
i
<
6
;
i
++
)
{
const
tLeft
=
vx
<
0
?
(
R
-
cx
)
/
vx
:
Infinity
...
...
@@ -36,37 +51,6 @@ function calcAimPoints(originX, originY, angle) {
return
points
}
/**
* 绘制发射器底座和指向瞄准角度的箭头
*/
function
drawLauncher
(
ctx
,
x
,
y
,
aimAngle
)
{
const
R
=
BUBBLE_RADIUS
ctx
.
save
()
ctx
.
beginPath
()
ctx
.
arc
(
x
,
y
,
R
*
1.25
,
0
,
Math
.
PI
)
ctx
.
fillStyle
=
'rgba(255,255,255,0.12)'
ctx
.
fill
()
ctx
.
strokeStyle
=
'rgba(255,255,255,0.25)'
ctx
.
lineWidth
=
1.5
ctx
.
stroke
()
ctx
.
translate
(
x
,
y
)
ctx
.
rotate
(
Math
.
PI
/
2
-
aimAngle
)
const
arrowTip
=
-
R
*
2.2
const
arrowBase
=
-
R
*
0.8
ctx
.
beginPath
()
ctx
.
moveTo
(
0
,
arrowTip
)
ctx
.
lineTo
(
-
R
*
0.35
,
arrowBase
)
ctx
.
lineTo
(
R
*
0.35
,
arrowBase
)
ctx
.
closePath
()
ctx
.
fillStyle
=
'rgba(255,255,255,0.5)'
ctx
.
fill
()
ctx
.
restore
()
}
/**
* 绘制虚线瞄准线
*/
function
drawAimLine
(
ctx
,
aimPoints
)
{
if
(
!
aimPoints
||
aimPoints
.
length
<
2
)
return
ctx
.
save
()
...
...
@@ -85,29 +69,62 @@ function drawAimLine(ctx, aimPoints) {
}
/**
* 绘制射击器
(底座、瞄准线、当前泡泡、下一颗预览)
* 绘制射击器
* @param {CanvasRenderingContext2D} ctx
* @param {Object} shooterState { aimAngle, isAiming, currentColor, nextColor
},可选 aimPoints;若不传则根据 aimAngle 计算
* @param {Object} shooterState { aimAngle, isAiming, currentColor, nextColor
, bounceScaleX, bounceScaleY }
*/
export
function
drawShooter
(
ctx
,
shooterState
)
{
if
(
!
shooterState
)
return
const
x
=
SCREEN_WIDTH
/
2
const
y
=
SCREEN_HEIGHT
-
CURRENT_Y_OFFSET
-
SAFE_AREA_BOTTOM
const
chickenBottom
=
SCREEN_HEIGHT
-
SAFE_AREA_BOTTOM
-
CHICKEN_BOTTOM_MARGIN
const
bubbleBaseY
=
chickenBottom
-
CHICKEN_HEIGHT
-
BUBBLE_TOP_OFFSET
const
R
=
BUBBLE_RADIUS
const
aimAngle
=
shooterState
.
aimAngle
??
Math
.
PI
/
2
const
isAiming
=
shooterState
.
isAiming
??
false
const
currentColor
=
shooterState
.
currentColor
??
1
const
nextColor
=
shooterState
.
nextColor
??
1
const
bounceScaleX
=
shooterState
.
bounceScaleX
??
1
const
bounceScaleY
=
shooterState
.
bounceScaleY
??
1
const
bounceOffsetY
=
(
1
-
bounceScaleY
)
*
CHICKEN_HEIGHT
/
2
const
aimPoints
=
shooterState
.
aimPoints
??
(
isAiming
?
calcAimPoints
(
x
,
y
,
aimAngle
)
:
[])
// 计算目标倾斜角度(跟随瞄准方向)
let
targetLean
=
0
if
(
isAiming
)
{
const
normalized
=
(
aimAngle
-
Math
.
PI
/
2
)
/
(
MAX_ANGLE
-
Math
.
PI
/
2
)
targetLean
=
-
normalized
*
MAX_LEAN_ANGLE
}
_currentLean
+=
(
targetLean
-
_currentLean
)
*
LEAN_LERP
// 呼吸浮动
_bobPhase
+=
BOB_SPEED
if
(
_bobPhase
>
Math
.
PI
*
2
)
_bobPhase
-=
Math
.
PI
*
2
const
bobOffset
=
Math
.
sin
(
_bobPhase
)
*
BOB_AMOUNT
// 瞄准线
const
aimPoints
=
shooterState
.
aimPoints
??
(
isAiming
?
calcAimPoints
(
x
,
bubbleBaseY
,
aimAngle
)
:
[])
drawAimLine
(
ctx
,
aimPoints
)
drawLauncher
(
ctx
,
x
,
y
,
aimAngle
)
drawBubble3D
(
ctx
,
x
,
y
,
R
,
currentColor
)
// 绘制鸡(以底部中心为旋转轴,跟随瞄准方向倾斜)
const
bottomY
=
chickenBottom
+
bounceOffsetY
+
bobOffset
ctx
.
save
()
ctx
.
translate
(
x
,
bottomY
)
ctx
.
rotate
(
_currentLean
)
ctx
.
scale
(
bounceScaleX
,
bounceScaleY
)
ctx
.
drawImage
(
_chickenImg
,
-
CHICKEN_WIDTH
/
2
,
-
CHICKEN_HEIGHT
,
CHICKEN_WIDTH
,
CHICKEN_HEIGHT
)
ctx
.
restore
()
// 当前待射泡泡(鸡头顶,跟随倾斜)
const
headX
=
x
+
Math
.
sin
(
_currentLean
)
*
CHICKEN_HEIGHT
*
bounceScaleY
const
headY
=
bottomY
-
Math
.
cos
(
_currentLean
)
*
CHICKEN_HEIGHT
*
bounceScaleY
-
BUBBLE_TOP_OFFSET
drawBubble3D
(
ctx
,
headX
,
headY
,
R
,
currentColor
)
// 下一颗预览泡泡
const
nextR
=
R
*
NEXT_SCALE
const
nextX
=
x
+
NEXT_X_OFFSET
const
nextY
=
y
+
NEXT_Y_OFFSET
const
nextY
=
chickenBottom
+
NEXT_Y_OFFSET
drawBubble3D
(
ctx
,
nextX
,
nextY
,
nextR
,
nextColor
)
ctx
.
save
()
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论