javascript五子棋小游戏
基于div+canvas的五子棋小游戏
示例地址:链接
开始
开始游戏:
主要逻辑:
1.绘制棋盘
javascript
/**
*
* 绘制棋盘
* @param {*} point
* */
function DrawBoard() {
points = []
ctx.beginPath();
// 绘制棋盘,14*14,每格50px*50px
for (let s = 0; s < counts + 1; s++) {
//先绘制横
ctx.moveTo(50, 50 * (s + 1));
ctx.font = "14px Arial";
ctx.lineTo(widths, 50 * (s + 1));
// 绘制横的文字,从15开始
ctx.fillText((counts + 1 - s), widths + 10, 50 * (s + 1)+4);
points.push([50, 50 * (s + 1), widths, 50 * (s + 1)]);
// 再绘制竖
ctx.moveTo(50 * s + 50, 50);
ctx.lineTo(50 * s + 50, heights);
// 绘制底部文字
ctx.fillText(bottomArray[s], 50 * s + 50-4, heights + 20);
}
ctx.closePath();
ctx.stroke();
if(!isStart)
OpenDialog("开始游戏");
}
2.绘制棋子
javascript
/**
* 绘制棋子
* **/
function DrawCircle(point2, isBlack = true) {
ctx.beginPath();
ctx.save();
// 绘制圆形
ctx.arc(point2.x, point2.y, gridWidth * 0.2, 0, 2 * Math.PI);
ctx.closePath();
// 绘制具体的颜色
ctx.shadowColor = SHADOW_COLOR;
ctx.shadowOffsetX = ctx.shadowOffsetY = gridWidth * 0.06; // 位置偏移来实现阴影效果
ctx.shadowBlur = gridWidth * 0.04;
const gradient = ctx.createRadialGradient(point2.x, point2.y, 0, point2.x, point2.y, gridWidth * 0.4); //利用镜像画面实现立体感
gradient.addColorStop(0, isBlack ? BLACK_CHESS_TOP_COLOR : WHITE_CHESS_TOP_COLOR);
gradient.addColorStop(1, isBlack ? BLACK_CHESS_COLOR : WHITE_CHESS_COLOR);
ctx.fillStyle = gradient;
ctx.fill();
ctx.restore();//保存最开始的状态,避免被影响---canvas相当于一个画笔,颜色之类的可能会改变
}
3.获取棋盘所有点
javascript
/**
* 获取棋盘所有交点的坐标
*
* **/
function getAllIntersectPoints() {
pointsAll = []
// 获取所有交点的坐标
for (let i = 0; i < points.length; i++) {
// 第一个为x轴,每个需要+50,第二个不变,3和4暂时不需要
// 一行15个点
for (let s = 0; s < 15; s++) {
pointsAll.push({ x: points[i][0] + (50 * s), y: points[i][1], radius: gridWidth * 0.5, tr: i, td: s, status: -1 })
}
}
}
4.开始游戏
javascript
// 棋子属性
BLACK_CHESS_COLOR = '#000000', // 黑棋底黑
BLACK_CHESS_TOP_COLOR = '#707070', // 黑棋顶灰,顶灰过渡到底黑+阴影=立体
WHITE_CHESS_COLOR = '#D5D8DC', // 白棋底灰
WHITE_CHESS_TOP_COLOR = '#FFFFFF', // 白棋顶白,顶白过渡到底灰+阴影=立体
SHADOW_COLOR = 'rgba(0, 0, 0, 0.5)', // 阴影颜色
canvas.onclick = function (e) {
x = e.offsetX;
y = e.offsetY;
if(!isStart){
if (x >= 370 && x <= 390 && y >= 361 && y <= 391) {
// 游戏开始
RestartDrawPiece();
}
}
if(!isStart){
return;
}
// 根据位置判断下是否在点位范围内
for (let m = 0; m < pointsAll.length; m++) {
var point2 = pointsAll[m]
// 判断是不是在点位范围,在就获取点位
if (Math.sqrt(Math.pow(x - point2.x, 2) + Math.pow(y - point2.y, 2)) <= point2.radius) {
if (pointsAll[m].status == -1) {
isBlack = !isBlack;
pointsAll[m].status = isBlack ? 1 : 0;
DrawCircle(pointsAll[m], isBlack);
if(canPlacePiece(isBlack, pointsAll[m].x, pointsAll[m].y)){
if(isBlack){
OpenDialog("黑子赢了");
} else {
OpenDialog("白子赢了");
}
isStart = false; // 游戏结束
isBlack = false; // 重置
}
}
}
}
}
/**
* 绘制提示弹框
* */
function OpenDialog(value){
ctx.beginPath();
ctx.rect(widths/2-150, heights/2-100, 300, 200);
ctx.fillStyle = "white";
ctx.fillRect(widths/2-150, heights/2-100, 300, 200);
ctx.stroke();
ctx.fillStyle = "black";
ctx.font = "20px Arial";
ctx.fillText(value, widths/2-40, heights/2-55);
// 绘制开始图标
ctx.beginPath();
ctx.arc(widths/2, heights/2, 20, 0, 2 * Math.PI);
ctx.stroke();
// 绘制箭头
ctx.beginPath();
ctx.moveTo(390, 376);
ctx.lineTo(370, 361);
ctx.lineTo(370, 391);
ctx.closePath();
ctx.stroke();
}
5.判断输赢
javascript
/**
* 检查输赢
* @params x,y->当前的点位
* @params role -> 黑棋还是白棋
* */
function canPlacePiece(isBlack=false,x=0,y=0) {
let status = isBlack ? 1 : 0;
// 需要将数组处理成二维数组,按行展开
// 先获取行
let trs = pointsAll.map(item => item['y'])
trs= Array.from(new Set(trs));
trs.sort((a,b) => {
return a - b
});
let txys = []
// 按行转化成二维数组
for(let s=0;s<trs.length;s++){
let arr = []
let arrs = []
for(let i=0;i<pointsAll.length;i++){
if(pointsAll[i].y === trs[s]){
arr.push(pointsAll[i])
}
arrs.push([i,s])
}
txys.push(arr)
}
// 检查横向
for(let i=0;i<txys.length;i++){
for(let j=0;j<txys[i].length;j++){
if(txys[i][j].status === status){
// 开始检查
if(j+4<txys[i].length){
// 检查横向
if(txys[i][j].status === txys[i][j+1].status && txys[i][j].status === txys[i][j+2].status && txys[i][j].status === txys[i][j+3].status && txys[i][j].status === txys[i][j+4].status){
return true;
}
}
if(i+4<txys.length){
// 检查垂直方向
if(txys[i][j].status === txys[i+1][j].status && txys[i][j].status === txys[i+2][j].status && txys[i][j].status === txys[i+3][j].status && txys[i][j].status === txys[i+4][j].status){
return true;
}
}
if(i+4<txys.length && j+4<txys[i].length){
// 左上右下
if(txys[i][j].status == txys[i+1][j+1].status && txys[i][j].status == txys[i+2][j+2].status && txys[i][j].status == txys[i+3][j+3].status && txys[i][j].status == txys[i+4][j+4].status){
return true
}
}
if(i-4>=0 && j+4<txys[i].length){
// 右上到左下方向
if(txys[i][j].status == txys[i-1][j+1].status && txys[i][j].status == txys[i-2][j+2].status && txys[i][j].status == txys[i-3][j+3].status && txys[i][j].status == txys[i-4][j+4].status){
return true
}
}
}
}
}
return false;
}
6.重新开始
javascript
/**
* 重新绘制,用来成功之后重新加载
*
*/
function RestartDrawPiece() {
isStart = true;
ctx.clearRect(0, 0, 800, 800);
DrawBoard();
getAllIntersectPoints();
}
7.完整代码
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
<style>
#canvas {
background: #f1f1f1;
}
</style>
</head>
<body>
<canvas id="canvas" height="800" width="800"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 先绘制横线,
// 设置棋盘宽度,高度,14*14
var widths = 750; // 宽度,这边多出的50是他的起始位置
var heights = 750; // 高度,这边多出的50是他的起始位置
var counts = 14; // 格数
var gridWidth = 50;
var bottomArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'h', 'j', 'k', 'l', 'm', 'n', 'o'];
var points = [];// 棋盘线所有点位
var pointsAll = []; // 棋子可以落点的位置
var savePiece = []; // 已保存的棋子
let isBlack = false; // 是否是黑棋
let successPiece = [];//成功的点位
let isStart = false; // 是否开始游戏
// 绘制棋盘
DrawBoard();
// 获取所有点位
getAllIntersectPoints();
// 棋子属性
BLACK_CHESS_COLOR = '#000000', // 黑棋底黑
BLACK_CHESS_TOP_COLOR = '#707070', // 黑棋顶灰,顶灰过渡到底黑+阴影=立体
WHITE_CHESS_COLOR = '#D5D8DC', // 白棋底灰
WHITE_CHESS_TOP_COLOR = '#FFFFFF', // 白棋顶白,顶白过渡到底灰+阴影=立体
SHADOW_COLOR = 'rgba(0, 0, 0, 0.5)', // 阴影颜色
canvas.onclick = function (e) {
x = e.offsetX;
y = e.offsetY;
if(!isStart){
if (x >= 370 && x <= 390 && y >= 361 && y <= 391) {
// 游戏开始
RestartDrawPiece();
}
}
if(!isStart){
return;
}
// 根据位置判断下是否在点位范围内
for (let m = 0; m < pointsAll.length; m++) {
var point2 = pointsAll[m]
// 判断是不是在点位范围,在就获取点位
if (Math.sqrt(Math.pow(x - point2.x, 2) + Math.pow(y - point2.y, 2)) <= point2.radius) {
if (pointsAll[m].status == -1) {
isBlack = !isBlack;
pointsAll[m].status = isBlack ? 1 : 0;
DrawCircle(pointsAll[m], isBlack);
if(canPlacePiece(isBlack, pointsAll[m].x, pointsAll[m].y)){
if(isBlack){
OpenDialog("黑子赢了");
} else {
OpenDialog("白子赢了");
}
isStart = false; // 游戏结束
isBlack = false; // 重置
}
}
}
}
}
// 此处是点击范围
// for (let m = 0; m < pointsAll.length; m++) {
// ctx.beginPath();
// var point2 = pointsAll[m]
// ctx.arc(point2.x, point2.y, point2.radius, 0, 2 * Math.PI);
// ctx.stroke();
// }
/**
*
* 绘制棋盘
* @param {*} point
* */
function DrawBoard() {
points = []
ctx.beginPath();
// 绘制棋盘,14*14,每格50px*50px
for (let s = 0; s < counts + 1; s++) {
//先绘制横
ctx.moveTo(50, 50 * (s + 1));
ctx.font = "14px Arial";
ctx.lineTo(widths, 50 * (s + 1));
// 绘制横的文字,从15开始
ctx.fillText((counts + 1 - s), widths + 10, 50 * (s + 1)+4);
points.push([50, 50 * (s + 1), widths, 50 * (s + 1)]);
// 再绘制竖
ctx.moveTo(50 * s + 50, 50);
ctx.lineTo(50 * s + 50, heights);
// 绘制底部文字
ctx.fillText(bottomArray[s], 50 * s + 50-4, heights + 20);
}
ctx.closePath();
ctx.stroke();
if(!isStart)
OpenDialog("开始游戏");
}
/**
* 获取棋盘所有交点的坐标
*
* **/
function getAllIntersectPoints() {
pointsAll = []
// 获取所有交点的坐标
for (let i = 0; i < points.length; i++) {
// 第一个为x轴,每个需要+50,第二个不变,3和4暂时不需要
// 一行15个点
for (let s = 0; s < 15; s++) {
pointsAll.push({ x: points[i][0] + (50 * s), y: points[i][1], radius: gridWidth * 0.5, tr: i, td: s, status: -1 })
}
}
}
/**
* 绘制棋子
* **/
function DrawCircle(point2, isBlack = true) {
ctx.beginPath();
ctx.save();
// 绘制圆形
ctx.arc(point2.x, point2.y, gridWidth * 0.2, 0, 2 * Math.PI);
ctx.closePath();
// 绘制具体的颜色
ctx.shadowColor = SHADOW_COLOR;
ctx.shadowOffsetX = ctx.shadowOffsetY = gridWidth * 0.06; // 位置偏移来实现阴影效果
ctx.shadowBlur = gridWidth * 0.04;
const gradient = ctx.createRadialGradient(point2.x, point2.y, 0, point2.x, point2.y, gridWidth * 0.4); //利用镜像画面实现立体感
gradient.addColorStop(0, isBlack ? BLACK_CHESS_TOP_COLOR : WHITE_CHESS_TOP_COLOR);
gradient.addColorStop(1, isBlack ? BLACK_CHESS_COLOR : WHITE_CHESS_COLOR);
ctx.fillStyle = gradient;
ctx.fill();
ctx.restore();//保存最开始的状态,避免被影响---canvas相当于一个画笔,颜色之类的可能会改变
}
/**
* 重新绘制,用来成功之后重新加载
*
*/
function RestartDrawPiece() {
isStart = true;
ctx.clearRect(0, 0, 800, 800);
DrawBoard();
getAllIntersectPoints();
}
/**
* 绘制提示弹框
* */
function OpenDialog(value){
ctx.beginPath();
ctx.rect(widths/2-150, heights/2-100, 300, 200);
ctx.fillStyle = "white";
ctx.fillRect(widths/2-150, heights/2-100, 300, 200);
ctx.stroke();
ctx.fillStyle = "black";
ctx.font = "20px Arial";
ctx.fillText(value, widths/2-40, heights/2-55);
// 绘制开始图标
ctx.beginPath();
ctx.arc(widths/2, heights/2, 20, 0, 2 * Math.PI);
ctx.stroke();
// 绘制箭头
ctx.beginPath();
ctx.moveTo(390, 376);
ctx.lineTo(370, 361);
ctx.lineTo(370, 391);
ctx.closePath();
ctx.stroke();
}
/**
* 检查输赢
* @params x,y->当前的点位
* @params role -> 黑棋还是白棋
* */
function canPlacePiece(isBlack=false,x=0,y=0) {
let status = isBlack ? 1 : 0;
// 需要将数组处理成二维数组,按行展开
// 先获取行
let trs = pointsAll.map(item => item['y'])
trs= Array.from(new Set(trs));
trs.sort((a,b) => {
return a - b
});
let txys = []
// 按行转化成二维数组
for(let s=0;s<trs.length;s++){
let arr = []
let arrs = []
for(let i=0;i<pointsAll.length;i++){
if(pointsAll[i].y === trs[s]){
arr.push(pointsAll[i])
}
arrs.push([i,s])
}
txys.push(arr)
}
// 检查横向
for(let i=0;i<txys.length;i++){
for(let j=0;j<txys[i].length;j++){
if(txys[i][j].status === status){
// 开始检查
if(j+4<txys[i].length){
// 检查横向
if(txys[i][j].status === txys[i][j+1].status && txys[i][j].status === txys[i][j+2].status && txys[i][j].status === txys[i][j+3].status && txys[i][j].status === txys[i][j+4].status){
return true;
}
}
if(i+4<txys.length){
// 检查垂直方向
if(txys[i][j].status === txys[i+1][j].status && txys[i][j].status === txys[i+2][j].status && txys[i][j].status === txys[i+3][j].status && txys[i][j].status === txys[i+4][j].status){
return true;
}
}
if(i+4<txys.length && j+4<txys[i].length){
// 左上右下
if(txys[i][j].status == txys[i+1][j+1].status && txys[i][j].status == txys[i+2][j+2].status && txys[i][j].status == txys[i+3][j+3].status && txys[i][j].status == txys[i+4][j+4].status){
return true
}
}
if(i-4>=0 && j+4<txys[i].length){
// 右上到左下方向
if(txys[i][j].status == txys[i-1][j+1].status && txys[i][j].status == txys[i-2][j+2].status && txys[i][j].status == txys[i-3][j+3].status && txys[i][j].status == txys[i-4][j+4].status){
return true
}
}
}
}
}
return false;
}
// function checkWin(board, player) {
// // 遍历棋盘
// for (let i = 0; i < board.length; i++) {
// for (let j = 0; j < board[i].length; j++) {
// // 如果当前位置是当前玩家的棋子
// if (board[i][j].status === player) {
// // 检查水平方向
// if (j <= board[i].length - 5) {
// if (board[i][j].status === board[i][j+1].status && board[i][j].status === board[i][j+2].status && board[i][j].status === board[i][j+3].status && board[i][j].status === board[i][j+4].status) {
// return true;
// }
// }
// // 检查垂直方向
// if (i <= board.length - 5) {
// if (board[i][j].status === board[i+1][j].status && board[i][j].status === board[i+2][j].status && board[i][j].status === board[i+3][j].status && board[i][j].status === board[i+4][j].status) {
// return true;
// }
// }
// // 检查左上到右下方向
// if (i <= board.length - 5 && j <= board[i].length - 5) {
// if (board[i][j].status === board[i+1][j+1].status && board[i][j].status === board[i+2][j+2].status && board[i][j].status === board[i+3][j+3].status && board[i][j].status === board[i+4][j+4].status) {
// return true;
// }
// }
// // 检查右上到左下方向
// if (i >= 4 && j <= board[i].length - 5) {
// if (board[i][j].status === board[i-1][j+1].status && board[i][j].status === board[i-2][j+2].status && board[i][j].status === board[i-3][j+3].status && board[i][j].status === board[i-4][j+4].status) {
// return true;
// }
// }
// }
// }
// }
// // 如果没有找到连续的五个相同的棋子,则返回false
// return false;
// }
</script>
</body>
</html>