html5的canvas实现中国象棋

html5的canvas实现中国象棋

最近有了一个写中国象棋程序的想法,就根据canvas实现了一下。下面是最终效果:

 首先,分析页面布局。主要的是有一个显示图像的区域,左下角是显示当前选中的棋子的<p>标签,右下角是两个按钮。

对于图像区域的布局,首先为了让棋子的移动,红圈的移动,和棋盘之间不会互相影响。那我们就要创建三个图层,也就是用三个<canvas>根据相对布局放在同一个位置。

在css设定z-index的值来确定图层的上下。棋盘图层应该放到最下面,棋子图层由于要对点击事件进行相应,于是放在最上层。其余就是JavaScript功能的实现。

注意几点:

  1. 棋子的图片应该是一个透明背景图,这样呈现的效果才会真实(为了更加真实,棋子图片是我的3D渲染图);
  2. 在网页刚打开时会调用init()方法,但是图片可能会没有加载出来,会造成棋盘上棋子显示不全的情况。这时只需刷新几次网页就好了。
  3. 在之后的棋子移动过程中可能也会有棋子显示不出来的情况(但是,目前还没有遇到),如果遇到这种情况就需要点击“刷新”按钮。
  4. 悔棋目前不支持多步,只能悔棋一步。若想改成多步,可能需要用几个数组来存储棋子走过的位置等一些信息。
  5. 没有写对于胜利的判断,因为我觉得象棋的胜利已经很明显了。若想要添加,可以写一个win()方法,在每一次Remove()方法调用后都判断一次。

所以整个的代码如下:

<!DOCTYPE html>
<html>

	<head>
		<meta charset="UTF-8">
		<title>中国象棋</title>
		<style type="text/css">
			* {
				padding: 0;
				margin: 0;
			}
			
			#id1 {
				position: relative;
			}
			
			#canvasp {
				position: absolute;
				top: 0px;
				left: 0px;
				z-index: 2;
			}
			
			#btn1 {
				position: absolute;
				width: 50px;
				height: 30px;
				left: 450px;
			}
			
			#btn3 {
				position: absolute;
				width: 50px;
				height: 30px;
				left: 380px;
			}
			
			#id3 {
				position: absolute;
				top: 0px;
				left: 0px;
				z-index: 1;
			}
		</style>
	</head>

	<body>

		<div id="id1">
			<div id="canvasp">
				<canvas id="myCanvas" width="500" height="470" style="border:1px solid #d3d3d3;" onmousedown="show_coords(event)">
				Your browser does not support the HTML5 canvas tag.
				</canvas>
			</div>
			<div id="id2"><canvas id="canvas" width="500" height="470"></canvas></div>
			<div id="id3"><canvas id="canvas3" width="500" height="470"></canvas></div>
		</div>
		<div id="id2">
			<button id="btn1">刷新</button>
			<button id="btn3">悔棋</button>
			<p id="checkedItem">选中了啥</p>

		</div>

		<script>
			var c = document.getElementById("myCanvas");
			var ctx = c.getContext("2d");
			//图片
			var c2 = document.getElementById("canvas");
			var canvas = c2.getContext("2d");
			//提示方框
			var c3 = document.getElementById("canvas3");
			var canvas3 = c3.getContext("2d");
			//选中
			var checkedItem = document.getElementById("checkedItem");

			var img = new Image();
			img.src = "img/chess/象棋棋盘2.png";
			img.onload = imgfn; //图片加载完在执行

			var img2 = new Image();
			var qizi = ["红車", "红马", "红相", "红仕", "红帅",
				"红炮", "红兵", "黑車", "黑马", "黑象", "黑士",
				"黑将", "黑炮", "黑卒"
			]

			init();

			function changImgPath(size) {
				var imgapath = "img/chess/象棋" + qizi[size] + ".gif";
				img2.src = imgapath;
			}

			function imgfn() {
				canvas.drawImage(img, 0, 0, 500, 480)
			}

			function imgfn2(x, y) {
				ctx.drawImage(img2, x, y, 50, 50)
			}

			function imgfn3(x, y) {
				canvas3.drawImage(img, x, y, 70, 70)
			}

			var pre_x = 0;
			var pre_y = 0;
			var victory = false;
			var Pre_isChecked = false;
			var Pre_checkedItem = 0;
			var Now_checkedItem = -2; //默认选择空
			var Now_team = true; //true表示黑方
			var Pre_team = true; //true表示黑方
			var Red_runed = false; //红方走过了没有
			var Black_runed = false; //黑方走过了没有
			var qizi_remember = -2; //默认为没有
			var Remove_successed = false; //上一步走了没
			var pre_pre_index = [0, 0]; //记录悔棋

			var index = new Array(
				[7, 8, 9, 10, 11, 10, 9, 8, 7], 
				[-2, -2, -2, -2, -2, -2, -2, -2, -2], 
				[-2, 12, -2, -2, -2, -2, -2, 12, -2], 
				[13, -2, 13, -2, 13, -2, 13, -2, 13], 
				[-2, -2, -2, -2, -2, -2, -2, -2, -2], 
				[-2, -2, -2, -2, -2, -2, -2, -2, -2], 
				[6, -2, 6, -2, 6, -2, 6, -2, 6], 
				[-2, 5, -2, -2, -2, -2, -2, 5, -2], 
				[-2, -2, -2, -2, -2, -2, -2, -2, -2], 
				[0, 1, 2, 3, 4, 3, 2, 1, 0]
			)

			function repeat(x, y) {
				return index[y][x] >= 0;
			}

			function show_coords(event) {
				if(victory == false) {

					x = event.clientX
					y = event.clientY
					var qp_x = Math.round((x - 25) / 55);
					var qp_y = Math.round((y - 25) / 48);
					Now_checkedItem = index[qp_y][qp_x];
					Now_team = Now_checkedItem > 6;
					changImgPath(Pre_checkedItem)
					if(Pre_isChecked && (Now_team != Pre_team || Now_checkedItem == -2)) {
						//							alert(Now_team+"xxx"+Pre_team)
						pre_pre_index[0] = pre_x;
						pre_pre_index[1] = pre_y;
						Remove(qp_x, qp_y, Pre_checkedItem, Now_checkedItem);
					}

					canvas3.clearRect(0, 0, 500, 500);

					pre_x = qp_x;
					pre_y = qp_y;

					x = qp_x * 50 + 25;
					y = qp_y * 46 + 9;

					canvas3.strokeStyle = 'red';
					canvas3.font = "50px Georgia";
					canvas3.strokeText("⚪", x - 7, y + 42);
					checkedItem.innerHTML = qizi[index[qp_y][qp_x]]
					Pre_isChecked = repeat(qp_x, qp_y)
					Pre_checkedItem = index[qp_y][qp_x];
					Pre_team = Pre_checkedItem > 6;
				} else {
					alert("请重新开局。");
				}

			}
			var btn1 = document.getElementById('btn1');
			var btn3 = document.getElementById("btn3");

			function clear(x, y) {
				x = x * 50 + 25;
				y = y * 46 + 9;
				ctx.clearRect(x, y + 3, 50, 47);
			}

			btn1.onclick = function() {
				imgfn2(pre_x * 50 + 25, pre_y * 46 + 9)
			}
			btn3.onclick = function() {
				if(!Remove_successed)
					return
				x = pre_pre_index[0];
				y = pre_pre_index[1];
				clear(pre_x, pre_y);
				imgfn2(x * 50 + 25, y * 46 + 9);
				if(qizi_remember != -2) {
					changImgPath(qizi_remember);
					setTimeout("imgfn2(" + pre_x + "*50+25," + pre_y + "*46+9)", 30)
				}
				index[pre_y][pre_x] = qizi_remember;
				index[y][x] = Pre_checkedItem;
				Pre_checkedItem = -2;
				if(Red_runed) {
					Red_runed = false;
					Black_runed = true;
					//					alert("调用了"+Red_runed)
				} else if(Black_runed) {
					Black_runed = false;
					Red_runed = true;
				}
			}

			function Remove(x, y, Pre_checkedItem, Now_checkedItem) {
				if(Red_runed) {
					if(Pre_checkedItem <= 6) {
						Remove_successed = false;
						return;
					}

				}
				if(Black_runed) {
					if(Pre_checkedItem > 6) {
						Remove_successed = false;
						return;
					}

				}

				if(!rule(x, y, Pre_checkedItem)) {
					Remove_successed = false;
					return;
				}
				Remove_successed = true;
				clear(pre_x, pre_y);
				if(Now_checkedItem != -2) {
					clear(x, y);
				}

				imgfn2(x * 50 + 25, y * 46 + 9)

				setTimeout("imgfn2(" + x + "*50+25," + y + "*46+9)", 15)

				qizi_remember = index[y][x]; //记录被吃掉的棋子或空白
				index[pre_y][pre_x] = -2;
				index[y][x] = Pre_checkedItem;

				if(Pre_checkedItem > 6) {
					Black_runed = true;
					Red_runed = false;
				}
				if(Pre_checkedItem <= 6) {
					Red_runed = true;
					Black_runed = false;
				}
			}

			function rule(x, y, Pre_checkedItem) {
				switch(Pre_checkedItem) {
					case 0:
					case 7:
						if(x == pre_x || y == pre_y) {
							if(x == pre_x) {
								var max_y = (y > pre_y) ? y : pre_y;
								var min_y = (y > pre_y) ? pre_y : y;
								for(var i = min_y + 1; i < max_y; i++) {
									if(index[i][x] != -2) {
										return false;
									}
								}
							}
							if(y == pre_y) {
								var max_x = (x > pre_x) ? x : pre_x;
								var min_x = (x > pre_x) ? pre_x : x;
								for(var i = min_x + 1; i < max_x; i++) {
									if(index[y][i] != -2) {
										return false;
									}
								}
							}
							return true;
						} else {
							return false;
						}
						break;
					case 1:
					case 8:
						if((Math.abs(x - pre_x) == 1 && Math.abs(y - pre_y) == 2) ||
							(Math.abs(x - pre_x) == 2 && Math.abs(y - pre_y) == 1)) {

							if((y - pre_y == 2 && index[pre_y + 1][pre_x] != -2) ||
								(y - pre_y == -2 && index[pre_y - 1][pre_x] != -2) ||
								(x - pre_x == 2 && index[pre_y][pre_x + 1] != -2) ||
								(x - pre_x == -2 && index[pre_y][pre_x - 1] != -2)) {
								return false;
							}
							return true;
						} else {
							return false;
						}
					case 2:
					case 9:
						if((Math.abs(x - pre_x) == 2 && Math.abs(y - pre_y) == 2)) {
							if((Pre_checkedItem > 6 && y <= 4) || (Pre_checkedItem <= 6 && y > 4)) {

								if((y > pre_y && x > pre_x && index[pre_y + 1][pre_x + 1] != -2) ||
									(y < pre_y && x > pre_x && index[pre_y - 1][pre_x + 1] != -2) ||
									(y > pre_y && x < pre_x && index[pre_y + 1][pre_x - 1] != -2) ||
									(y < pre_y && x < pre_x && index[pre_y - 1][pre_x - 1] != -2)) {
									return false;
								}
								return true;
							}
							return false;
						} else {
							return false;
						}
					case 3:
					case 10:
						if((Math.abs(x - pre_x) == 1 && Math.abs(y - pre_y) == 1)) {
							if((x >= 3 && x <= 5) && (y < 3 || y > 6)) {
								return true;
							}
							return false;
						} else {
							return false;
						}
					case 4:
					case 11:
						if((Math.abs(x - pre_x) == 1 && y == pre_y) ||
							(Math.abs(y - pre_y) == 1 && x == pre_x)) {
							if((x >= 3 && x <= 5) && (y < 3 || y > 6)) {
								return true;
							}
							return false;
						} else {
							return false;
						}
					case 5:
					case 12:
						if((x == pre_x || y == pre_y)) {
							var count2 = 0;
							if(x == pre_x) {
								var max_y = (y > pre_y) ? y : pre_y;
								var min_y = (y > pre_y) ? pre_y : y;
								for(var i = min_y + 1; i < max_y; i++) {
									if(index[i][x] != -2) {
										count2++;
										if(count2 > 1)
											return false;
									}
								}
							}
							if(y == pre_y) {
								var max_x = (x > pre_x) ? x : pre_x;
								var min_x = (x > pre_x) ? pre_x : x;
								for(var i = min_x + 1; i < max_x; i++) {
									if(index[y][i] != -2) {
										count2++;
										if(count2 > 1)
											return false;
									}
								}
							}
							if((count2 == 0 && Now_checkedItem == -2) ||
								(count2 == 1 && Now_checkedItem >= 0)) {
								return true;
							} else {
								return false;
							}
							return true;
						} else {
							return false;
						}
						break;
					case 6:
					case 13:
						var passRaiver = false;
						if(Pre_checkedItem > 6) { //黑色棋子
							passRaiver = pre_y > 4
							if(passRaiver) {
								if((Math.abs(x - pre_x) == 1 && y == pre_y) ||
									(Math.abs(y - pre_y) == 1 && x == pre_x)) {
									if(y - pre_y < 0) {
										return false;
									}
									return true;
								} else {
									return false;
								}
							} else {
								if(y - pre_y == 1 && x == pre_x) {
									return true;
								} else {
									return false;
								}
							}
							return true;
						} else {
							passRaiver = pre_y < 5
							if(passRaiver) {
								if((Math.abs(x - pre_x) == 1 && y == pre_y) ||
									(Math.abs(y - pre_y) == 1 && x == pre_x)) {
									if(y - pre_y > 0) {
										return false;
									}
									return true;
								} else {
									return false;
								}
							} else {
								if(y - pre_y == -1 && x == pre_x) {
									return true;
								} else {
									return false;
								}
							}
							return false;
						}

					default:
						alert("default了");
						return true;
						break;
				}
			}

			function init() {
				changImgPath(0)
				setTimeout("imgfn2(0*50+25,9*46+9)", 30)
				setTimeout("imgfn2(8*50+25,9*46+9)", 30)

				setTimeout("changImgPath(1)", 50)
				setTimeout("imgfn2(1*50+25,9*46+9)", 70)
				setTimeout("imgfn2(7*50+25,9*46+9)", 70)

				setTimeout("changImgPath(2)", 90)
				setTimeout("imgfn2(2*50+25,9*46+9)", 110)
				setTimeout("imgfn2(6*50+25,9*46+9)", 110)

				setTimeout("changImgPath(3)", 130)
				setTimeout("imgfn2(3*50+25,9*46+9)", 150)
				setTimeout("imgfn2(5*50+25,9*46+9)", 150)

				setTimeout("changImgPath(4)", 170)
				setTimeout("imgfn2(4*50+25,9*46+9)", 190)

				setTimeout("changImgPath(5)", 210)
				setTimeout("imgfn2(1*50+25,7*46+9)", 230)
				setTimeout("imgfn2(7*50+25,7*46+9)", 230)

				setTimeout("changImgPath(6)", 250)
				setTimeout("imgfn2(0*50+25,6*46+9)", 270)
				setTimeout("imgfn2(2*50+25,6*46+9)", 270)
				setTimeout("imgfn2(4*50+25,6*46+9)", 270)
				setTimeout("imgfn2(6*50+25,6*46+9)", 270)
				setTimeout("imgfn2(8*50+25,6*46+9)", 270)

				setTimeout("changImgPath(13)", 290)
				setTimeout("imgfn2(0*50+25,3*46+9)", 310)
				setTimeout("imgfn2(2*50+25,3*46+9)", 310)
				setTimeout("imgfn2(4*50+25,3*46+9)", 310)
				setTimeout("imgfn2(6*50+25,3*46+9)", 320)
				setTimeout("imgfn2(8*50+25,3*46+9)", 320)

				setTimeout("changImgPath(12)", 330)
				setTimeout("imgfn2(1*50+25,2*46+9)", 350)
				setTimeout("imgfn2(7*50+25,2*46+9)", 350)

				setTimeout("changImgPath(11)", 370)
				setTimeout("imgfn2(4*50+25,0*46+9)", 390)

				setTimeout("changImgPath(10)", 410)
				setTimeout("imgfn2(3*50+25,0*46+9)", 430)
				setTimeout("imgfn2(5*50+25,0*46+9)", 430)

				setTimeout("changImgPath(9)", 450)
				setTimeout("imgfn2(2*50+25,0*46+9)", 470)
				setTimeout("imgfn2(6*50+25,0*46+9)", 470)

				setTimeout("changImgPath(8)", 490)
				setTimeout("imgfn2(1*50+25,0*46+9)", 510)
				setTimeout("imgfn2(7*50+25,0*46+9)", 510)

				setTimeout("changImgPath(7)", 530)
				setTimeout("imgfn2(0*50+25,0*46+9)", 550)
				setTimeout("imgfn2(8*50+25,0*46+9)", 550)
			}
		</script>

	</body>

</html>

棋子图片和棋盘的图片已经上传到了我的百度网盘上了,链接如下:

链接:https://pan.baidu.com/s/1HwurAERgQiBt82WlH7L4IA 
提取码:bddb 
复制这段内容后打开百度网盘手机App,操作更方便哦

====================================================

2023年6月14日更新:

已更新链接;已将链接时长改为永久。

链接:https://pan.baidu.com/s/1EqKFkUxbvMhH5f4hYpwakw 
提取码:wng6

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值