前言: 之前对于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>