一、简介
本篇为对 JS实现双人五子棋简易游戏(上) 进行补充,添加了悔棋的功能,并修改部分页面效果。
若想实现人机的游戏方式可以看之前:JS实现五子棋单人双人简易游戏(下) 。
二、效果

三、代码
-
index.html
<!DOCTYPE HTML>
<html>
<head>
<title> 五子棋 </title>
<link rel="stylesheet" type="text/css" href="./css/style.css">
<link rel="stylesheet" href="./css/popup.css">
</head>
<body>
<div class="centre">
<canvas id='canvas' class="canvas" width='450' height='450'></canvas>
<button id="btnNew" class='btn'>新游戏</button>
<button id='btnClean' class="btn btnClean">悔棋</button>
<p class="txtClean">tip</p>
</div>
<!-- popup -->
<div id="popup">
<div class="pop">
<!-- popup-header -->
<div class="popup-header">
<p>tip</p>
<span id="btnClose" class="close">×</span>
</div>
<!-- popup-content -->
<div class="popup-content">
<p id="popup-txt" ></p>
<button id="consent">Yes</button>
<button id="dissent">No</button>
</div>
<!-- popup-footer -->
<div class="popup-footer"></div>
</div>
</div>
</body>
<script src="./js/tool.js"></script>
<script src="./js/popup.js"></script>
</html>
-
style.css
body{
margin: 0;
background-color: #eee;
}
.centre{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.canvas{
display: block; /* 块元素 */
margin: 20px auto;
background-color: #946136;
}
.btn{
position: relative;
margin: 15px 5px;
width: 100px;
height: 35px;
font-size: 16px;
color: #000000;
background-color: #ffffff;
border-radius:10px; /* 按钮圆角 */
border: none; /* 按钮边框 */
box-shadow: 1px 2px 2px #000000; /* 按钮添加阴影 */
}
.btn:hover {/* 按钮鼠标悬停 */
color: #000000;
cursor: pointer; /* 显示的光标的类型 */
box-shadow: 1px 5px 5px #ccc; /* 按钮添加阴影 */
border: 1px solid #000000; /* 按钮边框 */
}
.btn:active {/* 按钮鼠标点击 */
border: none; /* 按钮边框 */
transform: translateY(4px);/*对按钮进行移动*/
}
.txtClean{
padding: 8px;
display: inline; /* 行内元素 */
border: 2px solid #000000;
}
-
popup.css
#popup {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background: rgba(0, 0, 0, 0.5);
}
#popup .pop {
width: 300px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
animation: animate 0.01s;
}
.pop .popup-header {
height: 42px;
background: white;
color: #000;
line-height: 45px;
border-bottom: 1px solid #000000;
}
.pop .popup-header p {
display: inline-block;
margin: 0;
position: absolute;
left: 12px;
}
.pop .popup-header .close {
position: absolute;
right: 18px;
font-size: 20px;
cursor: pointer;
}
.pop .popup-content {
background: white;
height: 90px;
text-align: center;
}
.pop .popup-content p {
margin: 0;
padding: 10px;
}
.pop .popup-content button{
margin: 10px;
float: right;
width: 36px;
height: 24px;
color: #000000;
background-color: #ffffff;
border-radius: 2px;
border: 1px solid #000000;
box-shadow: 1px 2px 2px #ccc;
cursor: pointer; /* 显示的光标的类型 */
}
.pop .popup-footer {
position: relative;
height: 25px;
background: white;
border-top: 1px solid #000000;
}
/*添加动画*/
@keyframes animate {
from {
top: 0;
opacity: 0
}
to {
top: 50%;
opacity: 1
}
}
-
tool.js
var canvas = document.querySelector('#canvas');//获取画布元素
var btnNew = document.querySelector('#btnNew');//获取按钮元素
var btnClean = document.querySelector('#btnClean');//获取悔棋按钮
var ctx = canvas.getContext('2d');//获取绘制环境
var chesscolor = ['BLACK','WHITE'];//落子颜色
var step = 0;//记录当前步数
var flag = true;//记录游戏状态
var maparr = [];//记录棋盘状态
var chessXY = [, ];//记录落棋坐标
var txtClean = document.querySelector('.txtClean'); //tip文本
var popup = document.querySelector("#popup"); //弹窗
var btnClose = document.querySelector("#btnClose"); //弹窗关闭按钮
var popupContent = document.querySelector('.popup-content'); //弹窗主体
var popupTxt = document.querySelector('#popup-txt'); //弹窗文本
var consent = document.querySelector("#consent"); //弹窗确认按钮
var dissent = document.querySelector("#dissent"); //弹窗取消按钮
var numClean = 3; //悔棋次数
//检测棋子方向
var mode = [
[1,0],//水平
[0,1],//垂直
[1,1],//左上右下
[1,-1]//右上左下
];
//绘制文字
ctx.font="70px Arial";
ctx.fillText('Start Game!',40,235);
//开始新游戏
btnNew.onclick = function(){
consent.style.visibility="visible"; //显示弹窗按钮
dissent.style.visibility="visible"; //显示弹窗按钮
btnClose.style.visibility = "hidden"; //隐藏弹窗关闭按钮
popupTxt.innerHTML = "本局是否允许悔棋?"; //修改弹窗文本
popupContent.style = "height: 90px;"; //修改弹窗高度
popup.style.display = "block"; //显示弹窗
startGame();
cleanChessBoard();
drawChessBoard();
};
//悔棋按钮
btnClean.onclick = function(){
if(!flag){//判断游戏是否结束
consent.style.visibility="hidden"; //隐藏弹窗按钮
dissent.style.visibility="hidden"; //隐藏弹窗按钮
btnClose.style.visibility="visible"; //显示弹窗关闭按钮
popupTxt.innerHTML = "Game Over!"; //修改弹窗文本
popupContent.style = "height: 45px;"; //修改弹窗高度
popup.style.display = "block"; //显示弹窗
return;
}else if(numClean != 0){//判断能否悔棋
cleanChessBoard();
drawChessBoard();
cleanChess(chessXY);
}else{ //悔棋次数为0
consent.style.visibility="hidden"; //隐藏弹窗按钮
dissent.style.visibility="hidden"; //隐藏弹窗按钮
btnClose.style.visibility="visible";
popupTxt.innerHTML = "Can't do this!"; //修改弹窗文本
popupContent.style = "height: 45px;";
popup.style.display = "block";
return;
}
};
checkchess(canvas);
//开始游戏
function startGame(){
//记录棋盘状态
for(var i=0;i<14;i++){
maparr[i] = [];
for(var j=0;j<14;j++){
maparr[i][j] = 0;
}
}
flag = true;
step = 0;
numClean = 3;
}
//绘制棋盘
function drawChessBoard(){
for(var i=1;i<15;i++){/*绘制路径*/
ctx.moveTo(30*i,30);
ctx.lineTo(30*i,420);
ctx.moveTo(30,30*i);
ctx.lineTo(420,30*i);
}
ctx.stroke();/* 绘制(轮廓) */
ctx.beginPath();/* 提笔 */
}
//清除棋盘
function cleanChessBoard(){
ctx.fillStyle = '#946136';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
//落子机制
function checkchess(canvas){
canvas.addEventListener('click',function(e){
//棋子坐标
var px = Math.floor((e.offsetX+15)/30)-1;
var py = Math.floor((e.offsetY+15)/30)-1;
chessXY[0] = px;chessXY[1] = py;
//判断游戏是否结束
if(!flag){
consent.style.visibility="hidden"; //隐藏弹窗按钮
dissent.style.visibility="hidden"; //隐藏弹窗按钮
btnClose.style.visibility="visible";
popupTxt.innerHTML = "Game Over!";
popupContent.style = "height: 45px;";
popup.style.display = "block";
return;
}
//检测是否在棋盘上落子
if((px+1)*30==0 || (py+1)*30==0 || (px+1)*30==450 || (py+1)*30==450){
return;
}
if(maparr[px][py] == 0){
//落子
drawchess((px+1)*30,(py+1)*30,chesscolor[step%2]);
//记录落子颜色
maparr[px][py] = chesscolor[step%2];
//检测哪方胜负
for(var i=0;i<mode.length;i++){
checkwin(px,py,chesscolor[step%2],mode[i]);
}
step++;
}
});
}
//悔棋机制
function cleanChess(chessXY){
//修改棋盘状态
for(var x=0;x<14;x++){
for(var y=0;y<14;y++){
if(maparr[x][y] != 0){
if(x==chessXY[0] && y==chessXY[1]){
maparr[x][y] = 0;
--step;
--numClean;
txtClean.innerHTML = `tip:本局允许悔棋,剩余 <samp style="color: red;">`+numClean+`</samp> 次`;
continue;
}
drawchess((x+1)*30,(y+1)*30,maparr[x][y]);
}
}
}
}
//绘制棋子
function drawchess(x,y,color){//传入颜色,位置,绘制棋子
ctx.fillStyle = color;//填充颜色
ctx.arc(x,y,15,0,Math.PI*2,false);//画圆: 圆心 半径 弧度 逆时针
ctx.fill();//填充
//ctx.stroke();
ctx.beginPath();//提笔
}
//输赢机制
function checkwin(x,y,color,mode){//状态: 黑 白 没赢
var count = 0;//记录个数
for(var i=1;i<5;i++){
if(maparr[x+i*mode[0]]){
if(maparr[x+i*mode[0]][y+i*mode[1]] == color){
count++;
}else{
break;
}
}
}
for(var i=1;i<5;i++){
if(maparr[x-i*mode[0]]){
if(maparr[x-i*mode[0]][y-i*mode[1]] == color){
count++;
}else{
break;
}
}
}
//判断个数是否达到胜利个数
if(count >= 4){
consent.style.visibility="hidden"; //隐藏弹窗按钮
dissent.style.visibility="hidden"; //隐藏弹窗按钮
btnClose.style.visibility = "visible";
popupTxt.innerHTML = color + " WIN !";
popupContent.style = "height: 45px;";
popup.style.display = "block";
ctx.font="70px Arial";
ctx.fillText("Game Over!",40,235);
flag = false;//游戏结束
}
}
-
popup.js
window.onload = function () {
var txtClean = document.querySelector('.txtClean');
var consent = document.querySelector("#consent");
var dissent = document.querySelector("#dissent");
var btnClose = document.querySelector("#btnClose");
var popup = document.querySelector("#popup");
btnClose.onclick = function () {
popup.style.display = "none"; // 关闭popup
}
consent.onclick = function () {
popup.style.display = "none";
txtClean.innerHTML = `tip:本局允许悔棋,剩余 <samp style="color: red;">3</samp> 次`;
btnClean.disabled = false;
}
dissent.onclick = function () {
popup.style.display = "none";
txtClean.innerHTML = "tip:本局不允许悔棋";
btnClean.disabled = true;
}
}
四、结语
感兴趣的话可以点进来在线试下效果如何 五子棋 )