Canvas弹球小游戏
玩法介绍:通过键盘上下左右键控制小黑球移动,'吃掉’彩色弹球,彩色弹球相互碰撞交换颜色,按住Ctrl键可加速小黑球移动速度。
使用技术: Canvas(绘制画布、小球)、javaScript(创建/操控小球、碰撞检测等)
设计思路:
彩色弹球
- 创建一个弹球原型对象Ball(),在父类中定义每一个弹球的属性及事件,每一个弹球ball是’子’,创建后就有父类的属性和方法
let ball = new Ball()
- init:定义弹球一些初始属性,随机的初始位置/初速度/随机颜色
- draw:通过canvas来绘制彩色弹球
- move:弹球间的碰撞检测处理,包括与小黑球的碰撞(碰撞后被清除)/彩色弹球之间的碰撞(改变颜色)/与画布边界的碰撞
操控小黑球
controlBall:控制小球的移动,并做边界处理,按下ctrl键增大小黑球的速度speed
完整代代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>弹球</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>弹球</h1>
<h2></h2>
<h2 style="top: 60px;">方向键移动黑球,Ctrl键加速移动</h2>
<canvas id="canvas"></canvas>
<div id="conBall" style="top: 300px;left: 800px;width: 15px;"></div>
<script src="main.js">
</script>
</body>
</html>
var can = document.getElementById("canvas");
var ctx = can.getContext("2d");
var conBall = document.getElementById("conBall");
var h2 = document.querySelector("h2");
var dir = 0; //小黑球移动方向
var speed = 15; //小黑球速度
var num = 25;
var aBall = [];
var aColor = [
"rgb(136, 24, 241)",
"rgb(247, 11, 133)",
"rgb(1, 81, 255)",
"rgb(253, 248, 0)",
"rgb(246, 0, 0)",
"rgb(53, 232, 53)",
];
var w, h;
// 获取小黑球的圆心坐标和半径
var offCl = conBall.offsetLeft;
var offCt = conBall.offsetTop;
var cr = parseInt(conBall.style.width) / 2;
var cx = offCl + cr;
var cy = offCt + cr;
~~(function setSize() {
window.onresize = arguments.callee;
w = can.width = window.innerWidth;
h = can.height = window.innerHeight;
})();
// 生成随机数的函数
function random(min, max) {
const num = Math.floor(Math.random() * (max - min)) + min;
return num;
}
/*
创建小球对象
*/
function Ball() {}
// 在原型对象中设置小球的初始属性
Ball.prototype = {
// 小球的初始位置属性
init: function () {
this.x = random(30, w);
this.y = random(30, h);
this.r = random(8, 20);
this.vx = random(-5, 5);
this.vy = random(-5, 5);
this.crash = false;
if (this.vx == 0 && this.vy == 0) {
this.vx = 1;
this.vy = 1;
}
this.color = aColor[Math.floor(random(0, 6))];
},
// 绘制初始小球
draw: function () {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
},
// 小球移动
move: function () {
var mItem = this;
// 弹球与操控小球的碰撞
if (
Math.sqrt(Math.pow(this.x - cx, 2) + Math.pow(this.y - cy, 2)) <
this.r + cr
) {
this.clearBall();
} else {
//弹球之间的碰撞
for (var item of aBall) {
if (mItem != item) {
if (
Math.sqrt(
Math.pow(item.x - mItem.x, 2) + Math.pow(item.y - mItem.y, 2)
) <
item.r + mItem.r &&
mItem.crash == false &&
item.crash == false
) {
mItem.crash = true;
item.crash = true;
var nColor = aColor[Math.floor(random(0, 6))];
item.color = mItem.color = nColor;
item.vx = -item.vx;
item.vy = -item.vy;
mItem.vx = -mItem.vx;
mItem.vy = -mItem.vy;
}
}
mItem.crash = false;
item.crash = false;
}
this.x += this.vx;
this.y += this.vy;
// 水平碰撞
if (this.x - this.r < 0 || this.x + this.r > w) {
this.vx = -this.vx;
}
// 垂直碰撞
if (this.y - this.r < 0 || this.y + this.r > h) {
this.vy = -this.vy;
}
this.draw();
}
},
// 清除小球
clearBall: function () {
aBall.splice(aBall.indexOf(this), 1);
},
};
// 创建弹球函数
function creatBall(num) {
for (var i = 0; i < num; i++) {
var ball = new Ball();
ball.init();
ball.draw();
aBall.push(ball);
}
}
var timer = requestAnimationFrame(function fn() {
h2.innerHTML = "还剩余" + aBall.length + "个弹球";
// 绘制蒙版
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.fillRect(0, 0, w, h);
for (var item of aBall) {
item.move();
}
controlBall();
timer = requestAnimationFrame(fn);
});
creatBall(num);
/*
操纵小黑球
*/
function controlBall() {
switch (dir) {
case "ArrowLeft":
if (conBall.offsetLeft < 5) {
break;
} else {
conBall.style.left = conBall.offsetLeft - speed + "px";
offCl = conBall.offsetLeft;
cx = offCl + cr;
break;
}
case "ArrowDown":
if (conBall.offsetTop > h - 30) {
break;
} else {
conBall.style.top = conBall.offsetTop + speed + "px";
offCt = conBall.offsetTop;
cy = offCt + cr;
break;
}
case "ArrowRight":
if (conBall.offsetLeft > w - 30) {
break;
} else {
conBall.style.left = conBall.offsetLeft + speed + "px";
offCl = conBall.offsetLeft;
cx = offCl + cr;
break;
}
case "ArrowUp":
if (conBall.offsetTop < 5) {
break;
} else {
conBall.style.top = conBall.offsetTop - speed + "px";
offCt = conBall.offsetTop;
cy = offCt + cr;
break;
}
}
}
document.onkeydown = function (event) {
// 按下ctrl键加速
if (event.ctrlKey) {
speed = 18;
} else {
speed = 10;
}
dir = event.key;
};
document.onkeyup = function () {
dir = 0;
};
html, body {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-family: sans-serif;
height: 100%;
}
body {
overflow: hidden;
height: inherit;
}
h1 {
font-size: 32px;
letter-spacing: -1px;
position: absolute;
margin: 0;
top: -4px;
right: 5px;
color: transparent;
text-shadow: 0 0 3px white;
}
h2 {
font-size: 20px;
letter-spacing: -1px;
position: absolute;
margin: 0;
top: 35px;
right: 5px;
color: transparent;
text-shadow: 0 0 2.5px white;
}
#canvas{
background-color: #000;
display: block;
}
#conBall{
height: 15px;
background-color: #000;
border: 4px solid #fff;
border-radius: 15px;
position: absolute;
box-shadow: 0px 2px 2px 0px #fff,
0px -2px 2px 0px #fff,
2px 0px 2px 0px #fff,
-2px 0px 2px 0px #fff;
}