-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.js
351 lines (316 loc) · 8.95 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
// 设置画布
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const width = canvas.width = window.innerWidth
const height = canvas.height = window.innerHeight
// 定义关于 Ball 的常量,可设置
let ballConfig = {
maxLen: 6, // 球的个数
minSize: 25, // 球的最小半径
maxSize: 35, // 球的最大半径
minVel: 1, // 球的最小速度
maxVel: 4, // 球的最大速度
}
// 定义关于 ctx 的常量
let ctxConfig = {
bgColor: 'rgba(0, 0, 0)', // 背景颜色
transColor: 'rgba(0, 0, 0, 0.25)' // 球的运动过渡背景颜色,透明度越高,球的运动轨迹越明显
}
// 定义关于 User 的常量
let userConfig = {
life: 5, // 初始生命值
score: 0 // 初始得分
}
// 定义关于 颜色 衍生的属性,5种颜色,随机获取
let colorConfig = [ // 可自行扩展
{
color: 'white',
click: 1, // 需要点击的次数
// clicked: 0, // 已点击的次数,每次调用randomColor()时初始化clicked
score: -1, // 点击一次的得分
life: -1, // 点击一次得的生命值
border: 0 // 触到下边界得的生命值
},
{
color: 'grey',
click: 1,
// clicked: 0,
score: 1,
life: 0,
border: -1
},
{
color: 'green',
click: 1,
// clicked: 0,
score: 1,
life: 1,
border: 0
},
{
color: 'yellow',
click: 2,
// clicked: 0,
score: 1,
life: 0,
border: -2
},
{
color: 'red',
click: 3,
// clicked: 0,
score: 1,
life: 0,
border: -3
}
]
// 生成随机数的函数
function random(min,max) {
const num = Math.floor(Math.random() * (max - min)) + min
return num
}
// 生成随机颜色值的函数
function randomColor() {
// 先重置操作
const colorConfig2 = colorConfig.map((color) => {
color['clicked'] = 0 // 重置clicked次数
return color
})
// 随机颜色类0-5,random包括0不包括5
const color = colorConfig2[random(0, colorConfig2.length)]
return color
}
// 定义 User 构造器,默认生命值5,分数0
function User(name = 'SomeBody', life = userConfig.life, score = userConfig.score) {
this.name = name
this.life = life // 生命值
this.score = score // 得分
}
// 定义User更新函数
User.prototype.update = function(key, value = '') {
if (key in this) {
this[key] = value
}
}
// 定义User更新dom函数
User.prototype.updateText = function() {
const keys = ['name', 'life', 'score']
keys.forEach(key => {
if (document.getElementById(key)) {
document.getElementById(key).innerText = this[key]
}
})
}
// 定义 Ball 构造器
function Ball(x, y, velX, velY, color, size) {
this.x = x // x坐标
this.y = y // y坐标
this.velX = velX // x轴速度
this.velY = velY // y轴速度
this.color = color // 颜色类
this.size = size // 半径大小
}
// 定义彩球绘制函数
Ball.prototype.draw = function() {
ctx.beginPath()
ctx.fillStyle = this.color.color
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI)
ctx.fill()
}
// 定义彩球更新函数
Ball.prototype.update = function() {
// 是否触到下边界
if((this.y + this.size) >= height) {
const ball = this
// 球触到下边界的处理
borderWork(ball)
// 重置
const size = random(ballConfig.minSize, ballConfig.maxSize)
this.x = random(0 + size, width - size)
this.y = size
this.velX = random(ballConfig.minVel, ballConfig.maxVel)
this.velY = random(ballConfig.minVel, ballConfig.maxVel)
this.color = randomColor()
this.size = size
}
this.y += this.velY
}
// 定义碰撞检测函数
Ball.prototype.collisionDetect = function() {
for(let j = 0; j < balls.length; j++) {
if(this !== balls[j]) {
const dx = this.x - balls[j].x
const dy = this.y - balls[j].y
const distance = Math.sqrt(dx * dx + dy * dy)
// 检测两个小球是否相撞:两个小球中心的距离是否小于两个小球的半径之和
if (distance < this.size + balls[j].size) {
// 碰撞则改变颜色
balls[j].color = this.color = randomColor()
}
}
}
}
//点击事件
canvas.onclick=function(e){
for(let j = 0; j < balls.length; j++) {
const dx = e.offsetX - balls[j].x
const dy = e.offsetY - balls[j].y
const distance = Math.sqrt(dx * dx + dy * dy)
// 是否在此球和其周围发生的点击事件
if (distance < balls[j].velY + 2 * balls[j].size) {
console.log('distance', distance)
balls[j].color.clicked += 1
// 满足点击次数要求
if (balls[j].color.clicked >= balls[j].color.click) {
const ball = balls[j]
// 点击球的处理
clickWork(ball)
// 重置
const size = random(ballConfig.minSize, ballConfig.maxSize)
balls[j].x = random(0 + size, width - size)
balls[j].y = size
balls[j].velX = random(ballConfig.minVel, ballConfig.maxVel)
balls[j].velY = random(ballConfig.minVel, ballConfig.maxVel)
balls[j].color = randomColor()
balls[j].size = size
}
}
}
}
// 球触到下边界的处理
function borderWork(ball) {
// console.log('borderWork ball', ball)
if (user.life <= 0) return
user.life += ball.color.border
console.log('color: ' + ball.color.color + ',border: ' + ball.color.border + ',life: ' + user.life)
user.life = user.life < 0 ? 0 : user.life
user.updateText()
if (user.life <= 0) {
// 需使用线程调用pauseGame()
const st = setTimeout(() => {
pauseGame()
clearTimeout(st)
alert('游戏结束')
}, 100)
}
}
// 点击球的处理
function clickWork(ball) {
// console.log('clickWork ball', ball)
if (user.life <= 0) return
user.score += ball.color.score
user.life += ball.color.life
console.log('color: ' + ball.color.color + ',click: ' + ball.color.click+ ',clicked: ' + ball.color.clicked + ',life: ' + user.life)
user.life = user.life < 0 ? 0 : user.life
user.updateText()
if (user.life <= 0) {
// 需使用线程调用pauseGame()
const st = setTimeout(() => {
pauseGame()
clearTimeout(st)
alert('游戏结束')
}, 100)
}
}
// 定义玩家
let user = {}
function initUser() {
user = JSON.parse('{}')
user = new User('MONSTER')
user.updateText()
}
// 定义一个数组,生成并保存所有的球
let balls = []
function initBalls() {
balls = JSON.parse('[]')
while(balls.length < ballConfig.maxLen) {
const size = random(ballConfig.minSize, ballConfig.maxSize)
let ball = new Ball(
// 为避免绘制错误,球至少离画布边缘球本身一倍宽度的距离
random(0 + size, width - size),
size,
random(ballConfig.minVel, ballConfig.maxVel),
random(ballConfig.minVel, ballConfig.maxVel),
randomColor(),
size
)
balls.push(ball)
}
console.log('balls', balls)
ctx.fillStyle = ctxConfig.bgColor
ctx.fillRect(0, 0, width, height)
}
// 动画兼容性
let requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
let cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame
// 定义一个循环来不停地播放
let myReq
function loop() {
// 将整个画布的颜色设置成半透明的黑色,在下一个视图画出来时用来遮住之前的视图的。如果不这样做得话,你就会在屏幕上看到一条蛇的形状而不是小球的运动了。
ctx.fillStyle = ctxConfig.transColor
ctx.fillRect(0, 0, width, height)
for(let i = 0; i < balls.length; i++) {
balls[i].draw()
balls[i].update()
balls[i].collisionDetect()
}
myReq = requestAnimationFrame(loop)
}
// 开始动画
function startGame() {
if (user.life <= 0) {
alert('生命值为0,请点击重来')
return
}
myReq = requestAnimationFrame(loop)
}
// 暂停动画
function pauseGame() {
if(myReq) {
cancelAnimationFrame(myReq)
myReq = null
}
}
// 重来
function againGame() {
pauseGame()
initBalls()
initUser()
}
againGame()
// 玩法
function showIntroduce() {
showSomeDom('introduce')
}
// 设置
function showSetting() {
pauseGame()
showSomeDom('setting')
}
// 保存设置
function saveSetting() {
// 设置球相关属性
const keys = Object.keys(ballConfig)
keys.forEach(key => {
if (document.getElementsByName(key).length > 0) {
ballConfig[key] = Number(document.getElementsByName(key)[0].value)
}
})
console.log('ballConfig', ballConfig)
// 设置玩家相关属性
const userKeys = Object.keys(userConfig)
userKeys.forEach(key => {
if (document.getElementsByName(key).length > 0) {
userConfig[key] = Number(document.getElementsByName(key)[0].value)
}
})
console.log('userConfig', userConfig)
againGame()
showSomeDom('setting')
}
// 显示或隐藏dom
function showSomeDom(selector) {
const el = document.querySelector(selector)
el.style.visibility = el.style.visibility === 'visible' ? 'hidden' : 'visible'
}