弹跳小球
视频指导:【酷酷的Canvas动画-哔哩哔哩】 https://b23.tv/7eLF8hv
画一个小球
要实现弹跳小球,第一步当然是先使用canvas画出一个小球,用到的方法是canvas的arc方法来进行圆弧的绘制
比如我们想在canvas上绘制一个半径为100px的小球,可以使用以下代码:
// 绘制一个圆形
ctx.beginPath()
ctx.arc(200, 200, 100, 0, 2 * Math.PI, false)
// 绘制路径用stroke,填充路径用fill
ctx.fill() // 填充小球
ctx.closePath() // 闭合路径
通过上面的代码,我们画出了下面这样的一个小球
通过fillStyle属性和radius的值我们可以控制小球的颜色和大小。
画很多个小球
小球构造函数
上面我们使用4行代码画出了一个最简单的小球,那如果我们想要画很多个小球的话,不可能手动去一个小球一个小球的去写,因此,我们需要一个小球的构造函数,然后当我们需要一个新的小球的时候,直接new一个即可。
通过对小球的分析,我们可以知道,一个小球实例需要有以下的属性
- 圆心坐标(x,y)
- 半径r
- 颜色color
因为我们要实现的是弹跳小球,因此我们还需要给小球一个x方向的速度dx和y方向的速度dy
- x方向的速度dx
- y方向的速度dy
我们要把小球绘制到canvas上,因此还需一个draw方法,调用它就会根据小球上面的属性将小球绘制出来
- 绘制方法draw
小球弹跳的过程起始就是小球自身坐标xy的变化过程,因此我们还要有一个update方法来进行小球自身坐标的更新,这样小球才能动起来
- 更新方法update
通过上面的分析,小球的构造函数可以如下:
// 小球构造函数
function Ball(x, y, dx, dy, r, color) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.r = r
this.color = color
// 绘制方法
this.draw = function () {
ctx.beginPath()
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false)
cyx.fillStyle = this.color
ctx.fill() // 填充小球
ctx.closePath() // 闭合路径
}
// 更新方法
this.update = function () {
}
}
创建小球实例
尝试创建一个小球实例对象,并调用它的draw方法,看是否能在canvas上绘制出一个小球
const ball = new Ball(100,100,10,10,50,'green') // 绘制一个绿色的小球
ball.draw()
可以看到,是可以画出的,接下来我们进一步来完善小球的构造函数,比如说小球半径的大小是随机的,圆心坐标也是随机的,颜色也是随机的。
完善构造函数
半径大小及圆心坐标随机
因为canvas是有边界的,就是我们给canvas元素设置的宽高,所以我们的半径和圆心坐标都应该在canvas允许的范围内进行随机,因此,这里我们要思考一下边界条件。
边界条件
已知canvas的左上角是(0,0)原点,通过上图我们可以得知,
- 假设小球的左边刚好碰到矩形左边边界的成立条件是
x=r
- 假设小球的右边刚好碰到矩形右边边界的成立条件是
x+r=width
- 假设小球的上边刚好碰到矩形上边边界的成立条件是
y=r
- 假设小球的下边刚好碰到矩形下边边界的成立条件是
y+r=height
通过上面的分析,我们可以知道,在r确定的时候,圆心坐标的随机范围分别是
x坐标:[r,width - r]
y坐标:[r,height - r]
确定了范围之后,我们就可以实现获取指定范围的随机数方法了
// 获取指定范围的随机数
const giveMeRandom = (min, max) => {
// Math.random() * (max - min) + min
return Math.floor(Math.random() * (max - min + 1) + min)
}
小球颜色随机
小球颜色的随机方法就比较多了,选取较简单的一种,定义一个颜色数组,里面存放一定数量的颜色值,然后每次创建小球实例的时候,从中随机取出一个作为小球的颜色即可。实现代码如下:
// 颜色列表
const COLOR_LIST = ['#ff4858', '#1b7f79', '#00ccc0', '#72f2eb', '#747f7f']
// 随机获取颜色
const giveMeColor = () => {
const index = giveMeRandom(0, COLOR_LIST.length)
return COLOR_LIST[index]
}
绘制100个小球
接下里通过上面的分析,尝试绘制100个小球到canvas上,通过下面的代码
// 创建100个小球实例
const BALL_POOL = [] // 小球池-存放小球实例
for (let i = 0; i < 100; i++) {
const r = giveMeRandom(1, 15) // 确定小球半径
const x = giveMeRandom(r, canvas.width - r) // 确定x坐标
const dx = 10 // x方向速度
const y = giveMeRandom(r, canvas.height - r) // 确定y坐标
const dy = 10 // y方向速度
const color = giveMeColor() // 确定小球颜色
// 创建实例
BALL_POOL.push(new Ball(x, y, dx, dy, r, color))
}
// 绘制小球
for (let ball of BALL_POOL) {
ball.draw()
}
执行上述代码,得到的结果如下图
让小球动起来
上面构造函数那里我们也说过了,小球动起来其实就是小球自身坐标的运动,而我们眼中小球动起来,其实是画布不断刷新同时小球坐标在每次刷新时都不一样,又因为小球的坐标的变化是连续的,因此当刷新的频率达到一定的速度的时候,小球在我们眼中就像是动起来了一样,总结整个过程其实就是:
绘制 – 坐标更新 – 清空画布 – 绘制 – 坐标更新 – 清空画布 – 绘制 – ….
也就是说,我们要每隔一定的时间,将画布清空,然后重新将坐标改变后的小球实例绘制出来,这里要用到的就是专门用来绘制平滑动画的一个API了,它就是 requestAnimationFrame()
,这个API接收一个函数A作为参数,函数是要在重绘屏幕前调用的函数,我们对小球的坐标更新的操作就放在这个函数A中执行,又因为这个API只会调用一次传入的函数,因此我们要在每次调用完之后,再调用一次,以达到动画能连续的效果,代码如下:
// 动画函数
function animate() {
requestAnimationFrame(animate) // 调用自身,进而得到连续的动画
// ... 这里进行小球自身圆心坐标的更新操作
}
// 调用函数
animate()
到这里,我们就要回到小球的构造函数了,我们一开始在里面留了个update方法,用来进行小球自身坐标的更新,现在让我们来完善它。
update
这里就用到两个参数,分别是dx和dy,小球每次坐标变化多少就是由这两个参数决定的,所以,我们先尝试每次调用update的时候,给小球的xy坐标各自加上dx和dy试试看有什么效果。
// 更新方法
this.update = function () {
this.x += this.dx
this.y += this.dy
this.draw() // 改变完坐标要重新绘制
}
改变后的效果如下:
可见小球都往一个方向改变,而且沿途都留下了上一次绘制的痕迹,因此,我们要改变一下animate方法,同时在update中进行坐标更新的时候,要加上边界检测。
绘制前清空画布
// 动画函数
function animate() {
requestAnimationFrame(animate) // 调用自身,进而得到连续的动画
ctx.clearRect(0, 0, canvas.width, canvas.height) // 清空画布
// 绘制小球
for (let ball of BALL_POOL) {
ball.update()
}
}
更新坐标时进行边界检测
// 更新方法
this.update = function () {
// 左右边界检测
if (this.x < this.r || this.x > canvas.width - this.r) {
this.dx = -this.dx
}
// 上下边界检测
if (this.y < this.r || this.y > canvas.height - this.r) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
}
进行边界检测之后,小球就具有初步的弹跳效果了,如下图
但是很明显这里面的小球并不是无规则乱跳的,可以很明显的看出它们的跳动方向,这是因为,我们在初始化的时候,小球的运动方向并没有进行随机处理,而是采用了统一值,并且方向也是统一的(都是正值),因此我们可以修改一下创建小球实例的代码,使得小球的跳动更加自然。
const dx = (Math.random() - 0.5) * 20 // 0.5~-0.5 有正有负
const dy = (Math.random() - 0.5) * 20
最后得到的效果如图
贴上完整代码
暂无评论内容