canvas 绘制五子棋

112 阅读1分钟

前言:  之前对于canvas有所了解,比如咱们熟悉的大屏, 图片制作水印等等功能,这几天就具体的学习了一下,在学习的过程中看到很多大佬用canvas绘制五子棋,贪吃蛇等等等各种小游戏,然后在学习的过程中使用 Canvas实现了这个五子棋。

在此之前,确保自身有canvas基础
代码中含有大量注释

完整代码:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document </title>
	<style>
		canvas {
			background: #BB7B48;
			display: block;
			margin: 0 auto;
		}

		#div {
			position: absolute;
			top: 0;
			font-size: 30px;
			color: red;
		}
	</style>
</head>

<body>
	<canvas id="canvas"></canvas>
	<div id="div"></div>
	<script>
		const canvas = document.getElementById('canvas');
		const div = document.getElementById('div')
		canvas.width = 800;
		canvas.height = 800;
		const ctx = canvas.getContext('2d');
		// 绘制棋盘 上面定义800 格子是 50 一个 所以 800- 50 等于 750
		for (let i = 1; i < 16; i++) {
			// 横线 750 / 50
			ctx.moveTo(50, 50 * i);
			ctx.lineTo(750, 50 * i);
			ctx.stroke()
			// 纵线
			ctx.moveTo(50 * i, 50);
			ctx.lineTo(50 * i, 750);
			ctx.stroke()

		}
		canvas.addEventListener("contextlost", (event) => {
			event.preventDefault();
		});

		// 存储棋子
		let circles = [];

		for (let i = 1; i < 16; i++) {
			circles[i] = []
		}
		console.log('circles', circles);

		let flag = true;
		let endGanme = false
		canvas.addEventListener('click', (e) => {
			let {offsetX, offsetY} = e;
			// console.log('offsetX', offsetX, offsetY);
			if (offsetX < 50 || offsetY < 50 || offsetX > 750 || offsetY > 750) return;
			let i = Math.floor((offsetX + 25) / 50);
			let j = Math.floor((offsetY + 25) / 50);

			let x = i * 50;
			let y = j * 50;
			// console.log(i, j);
			// 最后一步写 同上 判断输赢
			if (endGanme) {
				return;
			}
			if (circles[i][j]) {
				console.log('存在重复落子');
				return;
			}

			circles[i][j] = flag ? 'black' : 'white'; // 用来判断输赢的 比如 横线 则是 [black,空][black,空][black,空][black,空][black,空] 这五个 black 连成一条线 则代表成功

			// console.log('circles', circles);
			ctx.beginPath()
			ctx.arc(x, y, 20, 0, 2 * Math.PI);
			let tx = flag ? x - 10 : x + 10;
			let ty = flag ? y - 10 : y + 10;
			// createRadialGradient参数 
			// x0开始圆形的 x 轴坐标。
			// y0开始圆形的 y 轴坐标。
			// r0开始圆形的半径。
			// x1结束圆形的 x 轴坐标。
			// y1结束圆形的 y 轴坐标。
			// r1结束圆形的半径
			const g = ctx.createRadialGradient(tx, ty, 0, tx, ty, 30); // 径向渐变 x,y
			g.addColorStop(0, flag ? '#ccc' : '#666'); // 填充颜色
			g.addColorStop(1, flag ? '#000' : '#fff'); // 填充颜色
			ctx.fillStyle = g;
			ctx.fill()
			ctx.closePath()
			// 最后判断棋子的落脚点 是否连成线
			// 判断当前是否已经有对应的棋子
			endGanme = checkVertical(i, j) || checkVertiRow(i, j) || checkNW(i, j) || checkNE(i, j);
			// 最后一步写 同上
			if (endGanme) {
				div.innerHTML = (flag ? '黑子' : '白子') + '胜利,请刷新页面'
				return;
			}
			flag = !flag
			divInnerHtml()
		})


		// 纵向
		function checkVertical(row, cal) {
			let up = 0;// 上
			let down = 0; // 下
			let count = 1; // 当前总共有几个连在一起
			// while(true) 死循环
			let times = 0;
			let target = flag ? 'black' : 'white';
			while (times < 100) {
				times++;



				up++;
				// x轴 上面一个的左边 判断是否连线
				if (circles[row][cal - up] && circles[row][cal - up] == target) {
					count++;
				}

				down++
				if (circles[row][cal + down] && circles[row][cal + down] == target) {
					count++;
				}
				// 判断是不是纵项连续的一条线
				if (count >= 5 || circles[row][cal - up] !== target && circles[row][cal + down] !== target) {
					break;
				};

			}
			return count >= 5
		}




		// 横向

		function checkVertiRow(row, cal) {
			let left = 0;// 上
			let right = 0; // 下
			// 左
			// 右
			let count = 1; // 当前总共有几个连在一起
			// while(true) 死循环
			let times = 0;
			let target = flag ? 'black' : 'white';
			while (times < 100) {
				times++;


				left++;
				// x轴 上面一个的左边 判断是否连线
				const rowLeft = circles[row - left] && circles[row - left][cal];
				const rowRight = circles[row + right] && circles[row + right][cal]
				if (rowLeft && circles[row - left][cal] == target) {
					count++;
				}

				right++
				if (rowRight && circles[row + right][cal] == target) {
					count++;
				}
				// 判断是不是纵项连续的一条线
				if (count >= 5 || rowLeft && circles[row - left][cal] !== target && rowRight && circles[row + right][cal] !== target) {
					break;
				};

			}
			return count >= 5
		}




		// 左上=>右下
		function checkNW(row, cal) {
			let times = 0;
			let lt = 0;
			let rb = 0;
			let count = 1;
			let target = flag ? 'black' : 'white';
			while (times < 100) {
				times++;
				lt++;
				if (circles[row - lt][cal - lt] && circles[row - lt][cal - lt] === target) {
					count++
				}
				rb++;
				if (circles[row + rb][cal + rb] && circles[row + rb][cal + rb] === target) {
					count++
				};
				if (count >= 5 ||
					circles[row - lt][cal - lt] !== target &&
					circles[row + rb][cal + rb] !== target) {
					break;
				};
			};
			return count >= 5
		}
		// 右上=>左下
		function checkNE(row, cal) {
			let times = 0;
			let rt = 0;
			let lb = 0;
			let count = 1;
			let target = flag ? 'black' : 'white';
			while (times < 100) {
				times++;
				rt++;
				if (circles[row + rt][cal - rt] && circles[row + rt][cal - rt] === target) {
					count++
				}
				lb++;
				if (circles[row - lb][cal + lb] && circles[row - lb][cal + lb] === target) {
					count++
				};
				if (count >= 5 ||
					circles[row + rt][cal - rt] !== target &&
					circles[row - lb][cal + lb] !== target) {
					break;
				};
			};
			return count >= 5
		}

		function divInnerHtml() {
			div.innerHTML = flag ? '请黑子落棋' : '请白子落棋'
		}
		divInnerHtml()
	</script>
</body>

</html>