绘制中国象棋棋盘
其实也没什么好写的, 权当水一篇博客吧, 顺便给有需要的人一个参考(虽然我是让 ai 画的).
象棋的话需要考虑到棋子的移动应该怎么处理. 如果是使用 svg 的话没什么烦恼, 直接移动就好了; 但是用的是 canvas, 有点小麻烦, 需要清除格子, 如果清除的不好的话会把压着的部分棋盘也清除掉, 这时候又得重画了. 问题是, 全部重画的话就会让棋盘盖在棋子上, 部分重画实现起来又很麻烦(除非一开始画的时候就是一块一块画). 其实可以分成两个图层, 棋盘在下面, 棋子在上面, 这样无论棋子怎么删除也不会影响到棋盘.
vue
<template>
<div class="relative w-full h-full">
<canvas
class="absolute left-1/2 top-1/4 -translate-x-1/2 -translate-y-1/4"
ref="background"
/>
<canvas
class="absolute left-1/2 top-1/4 -translate-x-1/2 -translate-y-1/4"
ref="chesses"
/>
</div>
</template>
background
作为棋盘, chesses
作为棋子.
这个问题解决了, 先画棋盘吧.
typescript
import ChessPiece from './ChessPiece';
import Drawer from './drawer';
class ChessBoard {
private board: number[][];
private gridSize: number;
// 棋盘
private background: CanvasRenderingContext2D;
// 棋子
private chesses: CanvasRenderingContext2D;
private pieces: ChessPiece[];
private width: number;
private height: number;
constructor(
background: CanvasRenderingContext2D,
chesses: CanvasRenderingContext2D,
gridSize: number = 50
) {
this.background = background;
this.chesses = chesses;
this.width = gridSize * 9;
this.height = gridSize * 10;
this.gridSize = gridSize;
this.board = Array.from({ length: 10 }, () => Array(9).fill(0)); // 初始化棋盘
this.pieces = []; // 棋子数组
this.initBoard();
}
public initBoard() {
this.drawBoard();
}
private drawBoard() {}
}
export default ChessBoard;
大概写出这样的代码, 然后丢给 ai 基本可以解决了. 这里要注意的是, 炮和兵位置需要特殊标记, 九宫格有斜线, 楚河汉界没有竖线. 记得提醒 ai.
拿到 ai 的代码之后大概率是不能如意的, 简单调整之后大概是这样子:
ts
private drawBoard() {
this.background.clearRect(0, 0, this.width, this.height)
this.background.strokeStyle = '#000'
this.background.lineWidth = 2
// Set background color
this.background.fillStyle = '#f4d1a4'
this.background.fillRect(0, 0, this.width, this.height)
const offsetX = this.gridSize / 2
const offsetY = this.gridSize / 2
const lineDrawer = Drawer.drawLine.bind(null, this.background)
// Draw horizontal lines
for (let i = 0; i < 10; i++) {
lineDrawer(
offsetX,
offsetY + i * this.gridSize,
offsetX + 8 * this.gridSize,
offsetY + i * this.gridSize,
)
}
// Draw vertical lines - but not across the river
for (let i = 0; i < 9; i++) {
lineDrawer(
offsetX + i * this.gridSize,
offsetY,
offsetX + i * this.gridSize,
offsetY + 4 * this.gridSize,
)
lineDrawer(
offsetX + i * this.gridSize,
offsetY + 5 * this.gridSize,
offsetX + i * this.gridSize,
offsetY + 9 * this.gridSize,
)
}
this.background.font = '20px Arial'
this.background.fillStyle = '#000'
this.background.textAlign = 'center'
this.background.fillText('楚 河', offsetX + 2 * this.gridSize, offsetY + 4.5 * this.gridSize)
this.background.fillText('汉 界', offsetX + 6 * this.gridSize, offsetY + 4.5 * this.gridSize)
// Draw the palaces (九宫)
// Top palace
lineDrawer(
offsetX + 3 * this.gridSize,
offsetY,
offsetX + 5 * this.gridSize,
offsetY + 2 * this.gridSize,
)
lineDrawer(
offsetX + 5 * this.gridSize,
offsetY,
offsetX + 3 * this.gridSize,
offsetY + 2 * this.gridSize,
)
lineDrawer(
offsetX + 3 * this.gridSize,
offsetY + 7 * this.gridSize,
offsetX + 5 * this.gridSize,
offsetY + 9 * this.gridSize,
)
lineDrawer(
offsetX + 5 * this.gridSize,
offsetY + 7 * this.gridSize,
offsetX + 3 * this.gridSize,
offsetY + 9 * this.gridSize,
)
lineDrawer(
offsetX + 0 * this.gridSize,
offsetY + 4 * this.gridSize,
offsetX + 0 * this.gridSize,
offsetY + 5 * this.gridSize,
)
lineDrawer(
offsetX + 8 * this.gridSize,
offsetY + 4 * this.gridSize,
offsetX + 8 * this.gridSize,
offsetY + 5 * this.gridSize,
)
// Draw the position markers for soldiers/pawns
this.drawPositionMarker(0, 3, offsetX, offsetY)
this.drawPositionMarker(2, 3, offsetX, offsetY)
this.drawPositionMarker(4, 3, offsetX, offsetY)
this.drawPositionMarker(6, 3, offsetX, offsetY)
this.drawPositionMarker(8, 3, offsetX, offsetY)
this.drawPositionMarker(0, 6, offsetX, offsetY)
this.drawPositionMarker(2, 6, offsetX, offsetY)
this.drawPositionMarker(4, 6, offsetX, offsetY)
this.drawPositionMarker(6, 6, offsetX, offsetY)
this.drawPositionMarker(8, 6, offsetX, offsetY)
// Draw the position markers for cannons
this.drawPositionMarker(1, 2, offsetX, offsetY)
this.drawPositionMarker(7, 2, offsetX, offsetY)
this.drawPositionMarker(1, 7, offsetX, offsetY)
this.drawPositionMarker(7, 7, offsetX, offsetY)
}
private drawPositionMarker(x: number, y: number, offsetX: number, offsetY: number) {
const markerSize = 5
const targetX = offsetX + x * this.gridSize
const targetY = offsetY + y * this.gridSize
const lineDrawer = Drawer.drawLine.bind(null, this.background)
if (x > 0) {
lineDrawer(
targetX - markerSize,
targetY - markerSize,
targetX - markerSize * 2,
targetY - markerSize,
)
lineDrawer(
targetX - markerSize,
targetY - markerSize,
targetX - markerSize,
targetY - markerSize * 2,
)
}
if (x < 8) {
lineDrawer(
targetX + markerSize,
targetY - markerSize,
targetX + markerSize * 2,
targetY - markerSize,
)
lineDrawer(
targetX + markerSize,
targetY - markerSize,
targetX + markerSize,
targetY - markerSize * 2,
)
}
if (x > 0) {
lineDrawer(
targetX - markerSize,
targetY + markerSize,
targetX - markerSize * 2,
targetY + markerSize,
)
lineDrawer(
targetX - markerSize,
targetY + markerSize,
targetX - markerSize,
targetY + markerSize * 2,
)
}
if (x < 8) {
lineDrawer(
targetX + markerSize,
targetY + markerSize,
targetX + markerSize * 2,
targetY + markerSize,
)
lineDrawer(
targetX + markerSize,
targetY + markerSize,
targetX + markerSize,
targetY + markerSize * 2,
)
}
}
drawPositionMarker
专门用来给炮和兵特殊标记, 这个Drawer
是我自己简单封装的一个类, 用来画画的.
ts
class Drawer {
public static drawLine(ctx: CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number) {
ctx.beginPath()
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
ctx.stroke()
}
public static drawCircle(ctx: CanvasRenderingContext2D, x: number, y: number, radius: number) {
ctx.beginPath()
ctx.arc(x, y, radius, 0, Math.PI * 2)
ctx.fill()
}
public static drawText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number) {
ctx.fillText(text, x, y)
}
}
export default Drawer
大概就这样, 浅浅水一下博客.