JS+Canvas实现接球小游戏的示例代码

作者:白露

时间:2026-10-25

浏览:484

本文详解如何使用HTML5 Canvas和JavaScript开发一个简单的接球小游戏。内容涵盖Canvas基础动画循环、利用requestAnimationFrame实现流畅渲染、鼠标事件监听实现挡板拖拽交互,以及核心的碰撞检测逻辑。通过分步解析代码,帮助开发者理解从图形绘制到物理交互的完整实现过程。

文章目录

写在最前

在浏览了Canvas动画系列教程后,创作一个小游戏的想法变得难以抑制。遵循一贯的学习路径——通过实践加深理解,加之近期未能进行篮球运动,便决定编写一个接球小游戏(严格来说,由于逻辑较为简单,称之为动画更为贴切)。这些实现均基于基础技术,读者若有兴趣,不妨动手尝试。言归正传,我们开始具体实现。

git地址

https://github.com/ry928330/ballGame.git

成果展示

写在最前

实现思路

此处采用疑问句形式梳理实现步骤,这也是开发该Demo时的思考路径:

- 如何在Canvas上绘制一个小球并使其产生运动效果?

- 如何在Canvas上绘制一个横条,并实现类似DOM元素的拖拽交互?

- 如何绘制并删除游戏界面上的小方块?

- 碰撞检测:如何判断小球与Canvas边界的碰撞?如何判断小球与底部横条的碰撞?如何判断小球与上方小方块的碰撞?(其底层原理基本一致)

主要完成上述四个步骤的实现,并将它们串联起来,即可得到一个简单的接球动画Demo。

详细说明

如何让小球动起来

这部分属于较为基础的Canvas动画范畴,一个基本的动画循环可归纳为以下步骤:

1. 清除Canvas:在每次绘制新图形前,必须清理之前的画面,从而产生逐帧重绘的视觉效果。

2. 保存当前状态:在每一步操作中,需保存当前Canvas的状态,包括图形的位置(x, y坐标)、尺寸(宽高)以及变换信息(如拉伸、旋转角度等)。

3. 重新渲染:在更新后的位置绘制目标图形。

4. 恢复Canvas状态:由于之前对Canvas信息进行了入栈保存,此时必须调用restore方法恢复状态。

仅完成上述四步是不够的。若将上述步骤封装名为draw的函数,要使图形动起来,还需借助以下三个函数之一:

setInterval(function, delay),setTimeout(function, delay),requestAnimationFrame(callback)

前端开发者对前两个函数可能较为熟悉,此处重点介绍第三个函数:requestAnimationFrame。该函数告知浏览器希望执行动画,并请求浏览器在下次重绘之前调用指定的回调函数。对于逐帧动画,推荐使用此方法。将draw函数作为回调传入requestAnimationFrame,即可实现draw函数的持续执行。

贴下我的部分代码:

//ball 对象用来存储一个球
var ball = {
	x: 150,
	y: 200,
	vx: 5, //水平速度
	vy: 5, //垂直速度
	radius: 20,
	color: 'blue',
	draw: function() {
		ctx.beginPath();
		ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
		ctx.closePath();
		ctx.fillStyle = this.color;
		ctx.fill();
	}
};
function draw() {
	ctx.clearRect(0,0, canvas.width, canvas.height);
	ball.draw();
	bar.draw();
	scores.draw();
	targetRectangle.draw();
	ball.x += ball.vx;
	ball.y += ball.vy;
	animationJudge = window.requestAnimationFrame(draw);
	...
}

draw函数内部包含了小球、横条及得分的绘制。每次绘制前会清空整个Canvas,并将各自的状态保存至对应的对象中。小球通过水平和垂直速度不断改变x和y坐标,从而形成运动效果。

绘制小横条儿,怎么像操作DOM一样去拖动它

虽然无法在Canvas中直接操作DOM元素,但可以对Canvas本身进行事件监听以获取位置信息。因此,实现方式与DOM拖拽类似:在鼠标移动过程中统计移动距离,改变横条位置并重新绘制,以此达到拖动效果。代码如下:

canvas.addEventListener('mousedown', function(e) {
		if (!beginGame) {
			draw();
			beginGame = true;
		}
		var x = e.clientX;
		var y = e.clientY;
		//判断拖拽的位置
		if ((x >=bar.x && x <= bar.x + bar.width) && (y >= bar.y && y <= bar.y + bar.height) ) {
			bar.barDragJudge = true;
			bar.xDistance = x;
		}

	})
	canvas.addEventListener('mousemove', function(e) {
		if (bar.barDragJudge) {
			var x = e.clientX;
			var distance = x - bar.xDistance;
			if (bar.x + distance + 60 >= canvas.width) {
				if (distance >= 0) {
					distance = 0;
				}
			} else if (bar.x + distance <= 10) {
				if (distance <= 0) {
					distance = 0;
				}
			}
			bar.x += distance;
			bar.xDistance = x;


		}
	})
	window.addEventListener('mouseup', function(e) {
		bar.barDragJudge = false;
	})

监听mousedown事件,当鼠标按下且位置与横条覆盖区域重合(游戏开始后绘制一次横条,页面相应位置会出现横条)时,将拖拽标志barDragJudge设置为true,表示允许拖拽。在鼠标移动过程中,通过计算移动距离更新横条位置,完成拖动,并限制横条不能移出Canvas边界。最后,在mouseup事件中,将barDragJudge设置为false。

如何实现游戏上面的小方块儿的绘制与删除?

绘制小方块本身并不复杂,只需使用二维数组存储矩形方块的信息。代码如下:

function initialTargetRectangleArr() {
		var targetRectangleArr = [];
		for (var i = 0; i < 4; i++) {
			targetRectangleArr[i] = [];
			for (var j = 0; j < 4; j++) {
				targetRectangleArr[i][j] = {};
				targetRectangleArr[i][j].x = 35 + j*(50 + 10);
				targetRectangleArr[i][j].y = 35 + i*(20 + 10);
				targetRectangleArr[i][j].width = 50;
				targetRectangleArr[i][j].height = 20;
			}
		}
		return targetRectangleArr;
	}

上述函数返回一个二维数组,数组元素为对象,每个对象包含小方块的位置及大小信息。

接下来说明小方块的删除。假设小球碰撞到小方块,碰撞后需将其擦除。

即借用clearRect函数擦除页面方块,但方块数据仍存在于内存中。为此,采取将方块“移出”Canvas的方式,即将其宽高设置为0。

碰撞问题

这是本Demo的关键,即如何判断小球与其他元素的碰撞。以下以小球与底部横条的碰撞为例进行说明。碰撞的核心在于位置判断,需准确获取横条在Canvas中的覆盖区域,当小球进入该区域时即判定为碰撞。代码如下(关键部分仅一行):

if ((ball.x + ball.vx >= bar.x - ball.radius && ball.x + ball.vx - ball.radius <= bar.x + bar.width) && (ball.y + ball.vy + ball.radius >= bar.y)) {
		ball.vy = -ball.vy;
	}

解析该代码:(ball.x + ball.vx >= bar.x - ball.radius && ball.x + ball.vx - ball.radius <= bar.x + bar.width) 主要判断两件事:&&号左侧判断小球落在横条左侧时,其右边缘是否在横条覆盖区域内;&&号右侧判断小球落在横条右侧时,其左边缘是否在横条覆盖范围内。在此基础上,(ball.y + ball.vy + ball.radius >= bar.y) 判断小球高度,若球心加上垂直移动速度及半径后的高度值大于等于横条的垂直高度,则判定碰撞发生。原理上,小球与Canvas边缘及小方块的碰撞与此类似,此处不再赘述。

关于计分及碰到红色小块加速的逻辑较为简单,可直接参考代码。至此,整个Demo基本实现。

写在最后

起初构思此Demo时,主要担心碰撞逻辑的实现难度。完成后发现其实较为简单。欢迎有时间的开发者从Git获取代码并在页面中体验,若发现Bug欢迎指正(主要担心碰撞边界设置不当可能引发问题)。接下来计划尝试难度升级,例如增加多个小球,观察持续重绘是否导致页面卡顿。当然,这是后话。

到此这篇关于JS+Canvas实现接球小游戏的示例代码的文章就介绍到这了,更多相关JS Canvas接球游戏内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章