一开始想着是通过构造函数来定义小球类,但是发现ES6的class关键字更高级和简便,但这只是简单的运用,如果有什么需要改进的地方或者不足,欢迎大家指出扶正。
首先,我们先定义一个父容器。
<div id="box"></div>
其次,就是给父容器设置定位及其它样式
#box {
width: 700px;
height: 500px;
margin: 100px auto 0;
border: 1px solid #FF0000;
position: relative;
}
最后就是js部分了,这里分为几块,因为是小练习,所以我并没有模块化。
首先,我们定义两个方法,生成随机数和随机色。
//随机数
function randomNum(x, y) {
let num = Math.floor(Math.random() * (y - x + 1) + x)
if (Math.abs(num) > 2) {
return num
} else {
return randomNum(x, y)
}
}
//随机色
function randomColor() {
let r = randomNum(0, 255)
let g = randomNum(0, 255)
let b = randomNum(0, 255)
return `rgb(${r},${g},${b})`
}
其次,定义小球类。
//共同属性:大小,颜色、位置、圆球、速度
//共同行为:移动、碰撞(collision)
class Ball {
constructor(size, color, ballLeft, ballTop, speedX, speedY) {
this.size = size
this.color = color
this.ballLeft = ballLeft
this.ballTop = ballTop
this.speedX = speedX
this.speedY = speedY
this.r = size / 2
}
ck = () => {
this.domBox = document.createElement("div")
this.domBox.style.width = this.size + "px"
this.domBox.style.height = this.size + "px"
this.domBox.style.borderRadius = "100%"
this.domBox.style.backgroundColor = this.color
this.domBox.style.position = "absolute"
box.appendChild(this.domBox)
}
move = () => {
let timer = setInterval(() => {
this.domBox.style.left = this.ballLeft + "px"
this.domBox.style.top = this.ballTop + "px"
this.ballLeft += this.speedX
this.ballTop += this.speedY
if (this.ballLeft >= 700 - this.size) {
this.speedX = -Math.abs(this.speedX)
}
if (this.ballLeft <= 0) {
this.speedX = Math.abs(this.speedX)
}
if (this.ballTop >= 500 - this.size) {
this.speedY = -Math.abs(this.speedY)
}
if (this.ballTop <= 0) {
this.speedY = Math.abs(this.speedY)
}
}, 10);
}
go = function() {
this.ck()
this.move()
}
}
然后就是让小球动起来了。
let ballArr = [],
coordArr = []
Array(5).fill(1).forEach(() => {
let ballSize = randomNum(40, 60),
ballLeft = randomNum(0, 640),
ballTop = randomNum(0, 440),
radius = ballSize / 2,
axiosX = ballLeft + radius,
axiosY = ballTop + radius,
judgeCoord = () => {
// 判断小球生成位置是否重叠
coordArr.forEach(item => {
if (Math.abs(item[0] - ballLeft) <= item[2] + radius) {
ballSize = randomNum(40, 60)
ballLeft = randomNum(0, 640)
judgeCoord()
}
if (Math.abs(item[1] - ballTop) <= item[2] + radius) {
ballSize = randomNum(40, 60)
ballTop = randomNum(0, 440)
judgeCoord()
}
})
}
judgeCoord()
// 记录球的坐标及半径
coordArr.push([axiosX, axiosY, radius])
let ball = new Ball(ballSize, randomColor(), ballLeft, ballTop, randomNum(-6, 6), randomNum(-6, 6))
ball.go()
ballArr.push(ball)
})
上面只是实现了小球与父容器的边缘碰撞检测,正常情况下,小球之间的相互碰撞也是需要判断的,所以我们在加个计时器判断每个小球是否碰撞
let isCrash = (obj1, obj2, ballArr) => {
let mainR = obj1.r + obj2.r
let distance = Math.sqrt(Math.pow((obj1.left + obj1.r - obj2.left - obj2.r), 2) + Math.pow((
obj1.top + obj1.r - obj2.top - obj2.r), 2))
if (mainR >= distance) {
let vx = obj1.speedX
let vy = obj1.speedY
ballArr[obj1.index].speedX = obj2.speedX
ballArr[obj2.index].speedX = vx
ballArr[obj1.index].speedY = obj2.speedY
ballArr[obj2.index].speedY = vy
}
return
}
this.setInterval(() => {
let ballProArr = ballArr.map((item, index) => {
return {
index,
left: item.ballLeft,
top: item.ballTop,
r: item.r,
speedX: item.speedX,
speedY: item.speedY
}
})
ballProArr.forEach((item, index) => {
let i = 1
let judgeBalls = () => {
if (index < ballProArr.length - i) {
isCrash(item, ballProArr[index + i], ballArr)
i++
judgeBalls()
}
return
}
judgeBalls()
})
}, 10)
效果图:
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#box {
width: 700px;
height: 500px;
margin: 100px auto 0;
border: 1px solid #FF0000;
position: relative;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
//随机数
function randomNum(x, y) {
let num = Math.floor(Math.random() * (y - x + 1) + x)
if (Math.abs(num) > 2) {
return num
} else {
return randomNum(x, y)
}
}
//随机色
function randomColor() {
let r = randomNum(0, 255)
let g = randomNum(0, 255)
let b = randomNum(0, 255)
return `rgb(${r},${g},${b})`
}
//共同属性:大小,颜色、位置、圆球、速度
//共同行为:移动、碰撞(collision)
class Ball {
constructor(size, color, ballLeft, ballTop, speedX, speedY) {
this.size = size
this.color = color
this.ballLeft = ballLeft
this.ballTop = ballTop
this.speedX = speedX
this.speedY = speedY
this.r = size / 2
}
ck = () => {
this.domBox = document.createElement("div")
this.domBox.style.width = this.size + "px"
this.domBox.style.height = this.size + "px"
this.domBox.style.borderRadius = "100%"
this.domBox.style.backgroundColor = this.color
this.domBox.style.position = "absolute"
box.appendChild(this.domBox)
}
move = () => {
let timer = setInterval(() => {
this.domBox.style.left = this.ballLeft + "px"
this.domBox.style.top = this.ballTop + "px"
this.ballLeft += this.speedX
this.ballTop += this.speedY
if (this.ballLeft >= 700 - this.size) {
this.speedX = -Math.abs(this.speedX)
}
if (this.ballLeft <= 0) {
this.speedX = Math.abs(this.speedX)
}
if (this.ballTop >= 500 - this.size) {
this.speedY = -Math.abs(this.speedY)
}
if (this.ballTop <= 0) {
this.speedY = Math.abs(this.speedY)
}
}, 10);
}
go = function() {
this.ck()
this.move()
}
}
let ballArr = [],
coordArr = []
Array(5).fill(1).forEach(() => {
let ballSize = randomNum(40, 60),
ballLeft = randomNum(0, 640),
ballTop = randomNum(0, 440),
radius = ballSize / 2,
axiosX = ballLeft + radius,
axiosY = ballTop + radius,
judgeCoord = () => {
// 判断小球生成位置是否重叠
coordArr.forEach(item => {
if (Math.abs(item[0] - ballLeft) <= item[2] + radius) {
ballSize = randomNum(40, 60)
ballLeft = randomNum(0, 640)
judgeCoord()
}
if (Math.abs(item[1] - ballTop) <= item[2] + radius) {
ballSize = randomNum(40, 60)
ballTop = randomNum(0, 440)
judgeCoord()
}
})
}
judgeCoord()
// 记录球的坐标及半径
coordArr.push([axiosX, axiosY, radius])
let ball = new Ball(ballSize, randomColor(), ballLeft, ballTop, randomNum(-6, 6), randomNum(-6, 6))
ball.go()
ballArr.push(ball)
})
let isCrash = (obj1, obj2, ballArr) => {
let mainR = obj1.r + obj2.r
let distance = Math.sqrt(Math.pow((obj1.left + obj1.r - obj2.left - obj2.r), 2) + Math.pow((
obj1.top + obj1.r - obj2.top - obj2.r), 2))
if (mainR >= distance) {
let vx = obj1.speedX
let vy = obj1.speedY
ballArr[obj1.index].speedX = obj2.speedX
ballArr[obj2.index].speedX = vx
ballArr[obj1.index].speedY = obj2.speedY
ballArr[obj2.index].speedY = vy
}
return
}
this.setInterval(() => {
let ballProArr = ballArr.map((item, index) => {
return {
index,
left: item.ballLeft,
top: item.ballTop,
r: item.r,
speedX: item.speedX,
speedY: item.speedY
}
})
ballProArr.forEach((item, index) => {
let i = 1
let judgeBalls = () => {
if (index < ballProArr.length - i) {
isCrash(item, ballProArr[index + i], ballArr)
i++
judgeBalls()
}
return
}
judgeBalls()
})
}, 10)
</script>
</html>
父容器和某些条件是写死的,需要的同学可以自行改进。
细心的同学会发现我并没有把小球之间的碰撞行为封装在小球类中,因为我暂时还没有找到解决思路,如果你有更好的思路,欢迎与我交流,一起进步!