<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>五子棋</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0">
<style>
#myCanvas {
background-color: oldlace;
box-shadow: 0 0 5px 0px rgba(0, 0, 0, .8);
border-radius: 5px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="470" height="470"></canvas>
<div></div>
<button onclick="backChess()">回退</button>
<button onclick="resetChess(100)">恢复棋局</button>
</body>
<script>
var canvas = document.querySelector('#myCanvas')
var context = canvas.getContext('2d')
let stepArr = [],
gap = 30,
edgeGap = 25,
// 记录棋盘上所有标记数字
boardArr = [],
// 一个小方格的斜边一半大小,用来判断棋子放置的位置
chessRadius = Math.sqrt(Math.pow(gap, 2) + Math.pow(gap, 2)) / 2,
// 棋子的半径大小
chessSize = 13
// let stepStorage = window.localStorage.getItem('step')
// if(stepStorage){
// stepArr = JSON.parse(stepStorage)
// }
/**
* 窗口坐标转换为Canvas坐标
* @param canvas
* @param x 窗口坐标x
* @param y 容器坐标y
* @return {{x: number, y: number}}
*/
function windowToCanvas(canvas, x, y) {
var bbox = canvas.getBoundingClientRect()
return {
x: (x - bbox.left) * canvas.width / bbox.width,
y: (y - bbox.top) * canvas.width / bbox.width
}
}
/**
* 根据数字标记转换成坐标值
*/
function cor2xy(ij) {
let arr = ij.split('-')
return {
x: arr[0] * gap + edgeGap,
y: arr[1] * gap + edgeGap
}
}
/**
* 画棋盘上的线
*/
function drawLine() {
context.strokeStyle = '#333'
context.strokeWidth = 1
for (let i = 0; i < 15; i++) {
let x1 = edgeGap + i * gap,
y1 = edgeGap,
x2 = x1,
y2 = edgeGap + gap * 14
context.moveTo(x1 + 0.5, y1)
context.lineTo(x2 + 0.5, y2)
}
for (let i = 0; i < 15; i++) {
let x1 = edgeGap,
y1 = edgeGap + i * gap,
x2 = edgeGap + gap * 14,
y2 = y1
context.moveTo(x1 + 0.5, y1)
context.lineTo(x2 + 0.5, y2)
}
for (let i = 0; i < 15; i++) {
let x = edgeGap + i * gap
let lineArr = []
for (let j = 0; j < 15; j++) {
let y = edgeGap + j * gap
boardArr.push(`${i}-${j}`)
}
}
context.stroke()
}
/**
* 画中心点
*/
function drawCenter() {
context.beginPath()
let x = edgeGap + 7 * gap,
y = edgeGap + 7 * gap
context.arc(x, y, 5, 0, 2 * Math.PI)
context.fillStyle = '#333'
context.fill()
}
/**
* 画棋子
*/
function drawChess(x, y, color) {
if (!color) {
color = 'black'
}
context.save()
context.beginPath()
context.shadowOffsetX = 0; // 阴影Y轴偏移
context.shadowOffsetY = 0; // 阴影X轴偏移
context.shadowBlur = 2; // 模糊尺寸
context.shadowColor = 'rgba(0, 0, 0, .5)'; // 颜色
context.fillStyle = color
console.log(color)
context.arc(x, y, chessSize, 0, 2 * Math.PI)
context.fill()
context.restore()
}
/**
* 在交点处画棋子
*/
function getNearestPos(pos) {
for (let i = 0; i < boardArr.length; i++) {
let ijStr = boardArr[i]
let bPos = cor2xy(ijStr)
let distance = Math.sqrt(Math.pow(pos.x - bPos.x, 2) + Math.pow(pos.y - bPos.y, 2))
if (distance <= chessRadius) {
if (stepArr.indexOf(ijStr) !== -1) {
return;
}
return {
bPos,
ijStr
}
}
}
}
function checkAllLink(ijStr, x, y, x2, y2) {
let ijArr = ijStr.split('-')
// 判断当前棋子的颜色,单数还是双数
let index = stepArr.indexOf(ijStr) % 2
// 四个角度判断棋子颜色是否连成5个
let linkSize = 1
for (let i = 1; i <= 4; i++) {
let newijStr = (parseInt(ijArr[0]) + i * x) + '-' + (parseInt(ijArr[1]) + i * y)
console.log(newijStr, stepArr.indexOf(newijStr))
if (stepArr.indexOf(newijStr) % 2 !== index) {
break;
}
linkSize++
}
for (let i = 1; i <= 4; i++) {
let newijStr = (parseInt(ijArr[0]) + i * x2) + '-' + (parseInt(ijArr[1]) + i * y2)
console.log(newijStr, stepArr.indexOf(newijStr))
if (stepArr.indexOf(newijStr) % 2 !== index) {
break;
}
linkSize++
}
return linkSize
}
/**
* 检查输赢
*/
function checkWin(ijStr) {
let size = checkAllLink(ijStr, 0, 1, 0, -1)
if (size >= 5) {
return true
}
size = checkAllLink(ijStr, 1, 0, -1, 0)
if (size >= 5) {
return true
}
size = checkAllLink(ijStr, 1, -1, -1, 1)
if (size >= 5) {
return true
}
size = checkAllLink(ijStr, 1, 1, -1, -1)
if (size >= 5) {
return true
}
return false
}
/**
* 初始化棋盘
*/
function drawBoard() {
context.beginPath()
context.fillStyle = '#f3c281'
context.fillRect(0, 0, canvas.width, canvas.height)
drawLine()
drawCenter()
}
function drawChessCover(pos) {
drawBoard()
stepArr.forEach((ijStr, index) => {
let bPos = cor2xy(ijStr)
if (index % 2 == 0) {
drawChess(bPos.x, bPos.y)
} else {
drawChess(bPos.x, bPos.y, 'white')
}
})
let drawChessTemp = function(x, y, color) {
context.save()
context.beginPath()
let fillColor = 'rgba(255,255,255,.5)'
if (color == 'black') {
fillColor = 'rgba(0,0,0,.5)'
}
context.fillStyle = fillColor
context.arc(x, y, chessSize, 0, 2 * Math.PI)
context.fill()
context.strokeStyle = color
context.stroke()
context.restore()
}
if (stepArr.length % 2 == 0) {
drawChessTemp(pos.x, pos.y, 'black')
} else {
drawChessTemp(pos.x, pos.y, 'white')
}
}
/**
* 下棋事件
*/
canvas.addEventListener('click', (e) => {
let pos = windowToCanvas(canvas, e.clientX, e.clientY)
let res = getNearestPos(pos)
if (!res) {
return
}
let {bPos, ijStr} = res
if (stepArr.length % 2 == 0) {
drawChess(bPos.x, bPos.y)
} else {
drawChess(bPos.x, bPos.y, 'white')
}
stepArr.push(ijStr)
// window.localStorage.setItem('step',JSON.stringify(stepArr))
console.log(stepArr)
if (checkWin(ijStr)) {
setTimeout(() => {
alert('赢了')
}, 300)
}
drawChessCover(pos)
})
canvas.addEventListener('mousemove', (e) => {
let pos = windowToCanvas(canvas, e.clientX, e.clientY)
let res = getNearestPos(pos)
if (!res) {
return
}
let {bPos, ijStr} = res
drawChessCover(bPos)
})
/**
* 回退一步棋
*/
function backChess() {
drawBoard()
stepArr.pop()
console.log(stepArr)
stepArr.forEach((ijStr, index) => {
let bPos = cor2xy(ijStr)
if (index % 2 == 0) {
drawChess(bPos.x, bPos.y)
} else {
drawChess(bPos.x, bPos.y, 'white')
}
})
}
/**
* 恢复棋局`
*/
function resetChess(timestamp) {
// 画棋盘
drawBoard()
// 恢复棋盘
stepArr.forEach((ijStr, index) => {
let bPos = cor2xy(ijStr)
setTimeout(() => {
if (index % 2 == 0) {
drawChess(bPos.x, bPos.y)
} else {
drawChess(bPos.x, bPos.y, 'white')
}
}, timestamp * index)
})
}
resetChess(100)
</script>
</html>