快捷目录
前两节中,我们利用 canvas 绘制出了线条和棋子。那么在这一节当中,我们将利用前两节所学的知识把棋盘构建出来。把布局全部搭建完毕后,用于去承载我们后续用于人工智能的逻辑性代码。
使用 canvas 绘制棋盘
我们在前两节中知道应该怎么去画线了,而一个棋盘是由 15 * 15 的盘面组成的。那按照正常逻辑来说,我们肯定是通过循环去绘制这个盘面,而不是一根根去画对吧。
那接下来我们就开始正式绘制棋盘,而为了跟棋子区分开来,我就不使用黑色去绘制了,改用灰色(context.strokeStyle
),并且要跟它说一声,我们要开始画了(context.beginPath()
):
xml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<canvas id="chess" width=800 height=800></canvas>
</body>
<script type="text/javascript">
var chess = document.getElementById("chess");
var context = canvas.getContext("2d");
context.strokeStyle = "#ccc";
context.beginPath()
</script>
</html>
我们需要绘制 15 条线,也就是说我们需要去循环 15 次:
scss
for (var i = 0; i < 15; i++) {
// 起点
context.moveTo(15, 15 + i * 30)
// 终点
context.lineTo(435, 15 + i * 30)
// 结束绘画
context.stroke()
}
然后我们再看看效果:
我们可以清楚地看到横线都出来了,那接下来我们来解释一下代码为什么要这么去写。我们需要绘制15条横线,那我们就从15开始( context.moveTo(15, 15 + i * 30)
),这里的15是x轴横坐标。那么纵坐标呢,我们就希望它慢慢地往下涨,每次涨 30,所以是15 + i * 30
。
那么我们终点的context.lineTo(435, 15 + i * 30)
为什么是 435 呢?我们每一行往下涨 30,然后一共涨 14 行,也就是 30 乘以 14 ,再加上我们第一行原本的 15,是不是就得到 435 了。不管是起点还是终点,我们绘制的都是横线,所以y轴都是不变的,就保持15 + i * 30
。
那我们的横线就解决了,接下来我们就得画竖线。那竖线其实跟我们的横线是差不多的,横线有了,那竖线是不是把x轴跟y轴反过来就可以了:
ini
var chess = document.getElementById("chess");
var context = chess.getContext("2d");
context.strokeStyle = "#ccc";
context.beginPath()
for (var i = 0; i < 15; i++) {
// 横线起点
context.moveTo(15, 15 + i * 30)
// 横线终点
context.lineTo(435, 15 + i * 30)
// 竖线起点
context.moveTo(15 + i * 30, 15)
// 竖线终点
context.lineTo(15 + i * 30, 435)
// 结束绘画
context.stroke()
}
我们来看看效果:
那我们棋盘就完成了,颜色有点淡的我们再调整一下。那我们棋盘做好了,接下来是不是就得落子了呢?哎,这个就稍微有点难度了。为什么麻烦呢?因为这个就需要涉及到计算了。在我们落子的时候,你肯定没办法点得那么准对吧,很难去做到刚好就落到我们期望的位置。
基本落子逻辑处理
我们可以弄一个 3030 的范围,只要你落在这个范围上,我都会认为你就是落到这个点上了。也就是说每个点都会有自己 3030 的范围,即中心点上下左右 15 的范围,这就是我们解决落子点的一个判断方法。
那接下来我们就开始写落子点了,首先这个肯定会有一个 onclick 点击事件,这个没问题吧。但是我们使用的是canvas,它不像 div,没办法单独给每个点去添加一个点击事件。所以我们只能给 canvas 去添加:
ini
chess.onclick = function (e) {
console.log(e.offsetX, e.offsetY);
}
上面的代码中,我们打印了一个offsetX
和offsetY
。offsetX
和offsetY
是相对于点击元素的左上坐标。然后我们定义变量把点击的坐标值除以我们格子的大小 30,再通过向下取整(Math.floor
):
ini
chess.onclick = function (e) {
// console.log(e.offsetX, e.offsetY);
var i = Math.floor(e.offsetX / 30)
var j = Math.floor(e.offsetY / 30)
console.log(i, j);
}
接下来我们来看看这个得到的坐标值是不是我们想要的效果:
可以看到当我们点击左上角的时候,我们得到的坐标值为(0,0);点击第二行第二列时,我们可以得到坐标值(2,2)。那我们得到坐标值x和y后,我们现在就可以去进行绘画了。
那我们画这个圆的时候,首先得留意一下,我们这个棋盘距离左边是不是有 15 的距离。因为每个格子的中心点上下左右都是 15,那么我们画圆的时候就得把这个 15 给算进去。结合下图看一下:
比如我们点击红框的范围,此时我们要框选出这个盒子,是不是就得拿i
乘以盒子的大小 30。但是我们还得加上棋盘距离左边那 15 的距离,所以我们这个圆的起始点x就得等于15+i*30
,同理起始点y就得等于15+j*30
。
半径的话我们使用 13,因为 15 刚好就是盒子的半径,我们尽量取比半径小点的值。
scss
// 复习一下
context.arc(圆心x轴的位置,圆心y轴的位置,半径,起始角度,终点角度,是否逆时针)
context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
然后我们再按照黑棋的做法给它加反光的效果:
ini
chess.onclick = function (e) {
var i = Math.floor(e.offsetX / 30)
var j = Math.floor(e.offsetY / 30)
var context = chess.getContext("2d");
context.beginPath();
// 画圆
context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
// 增加反光效果
var grd = context.createRadialGradient(15 + i * 30, 15 + j * 30, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0)
// 给反光效果加颜色
grd.addColorStop(0, 'black')
grd.addColorStop(1, 'white')
context.fillStyle = grd
context.fill()
context.stroke()
}
保存代码运行一下,我们来看看有没有落子成功。
我们可以看到棋子已经完美落地,而截至目前为止呢,我们把基本的布局基本都做完了。基本的落子逻辑也处理完了,但是这个逻辑目前还是存在问题的,我们只是简单地把落子的位置处理好了。
在本节中,我们完善了整个布局,并且已经开始运用简单的算法去处理落子的位置。那么在下一节中,我们将完善我们的落子逻辑,并且要对游戏的胜利规则进行算法的计算。
本节代码
xml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<canvas id="chess" width=800 height=800></canvas>
</body>
<script type="text/javascript">
var chess = document.getElementById("chess");
var context = chess.getContext("2d");
context.strokeStyle = "#666";
context.beginPath()
// 绘制棋盘
for (var i = 0; i < 15; i++) {
// 横线起点
context.moveTo(15, 15 + i * 30)
// 横线终点
context.lineTo(435, 15 + i * 30)
// 竖线起点
context.moveTo(15 + i * 30, 15)
// 竖线终点
context.lineTo(15 + i * 30, 435)
// 结束绘画
context.stroke()
}
// 落子
chess.onclick = function (e) {
// 横坐标
var i = Math.floor(e.offsetX / 30)
// 纵坐标
var j = Math.floor(e.offsetY / 30)
var context = chess.getContext("2d");
context.beginPath();
context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
var grd = context.createRadialGradient(15 + i * 30, 15 + j * 30, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0)
grd.addColorStop(0, 'black')
grd.addColorStop(1, 'white')
context.fillStyle = grd
context.fill()
context.stroke()
}
</script>
</html>