小细节大体验------棋盘上那些容易被忽略的视觉元素
标记截图:

星位:棋盘的"坐标参照物"
什么是星位
星位是棋盘上的特殊标记点,传统五子棋/围棋棋盘上有5个星位:
0 1 2 3 4 ... 7 ... 11 ... 14
┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
3 │ │ │ ★ │ │ │ │ ★ │ │
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
7 │ │ │ │ │ ★ │ │ │ │ │ ← 天元
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
11│ │ │ ★ │ │ │ │ ★ │ │
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
代码实现
typescript
const stars: number[][] = [[3, 3], [3, 11], [7, 7], [11, 3], [11, 11]];
ctx.fillStyle = STAR_COLOR;
for (const star of stars) {
const sx = halfCell + star[1] * cellSize;
const sy = halfCell + star[0] * cellSize;
ctx.beginPath();
ctx.arc(sx, sy, 3, 0, Math.PI * 2);
ctx.fill();
}
星位的作用
- 视觉参照:帮助玩家快速定位棋盘位置
- 美观性:增加棋盘的传统感
- 开局参考:天元(7,7)是常见的开局位置
星位坐标解析
[3, 3]:左上星位[3, 11]:右上星位[7, 7]:天元(中心)[11, 3]:左下星位[11, 11]:右下星位
这5个位置在15x15棋盘上呈对称分布,符合传统棋盘的美学。
星位半径
typescript
ctx.arc(sx, sy, 3, 0, Math.PI * 2);
固定3像素半径------不随cellSize变化。这是一个设计选择:星位保持固定大小,在不同屏幕上视觉一致。
最后一手标记
代码实现
typescript
if (this.lastMoveRow >= 0 && this.lastMoveCol >= 0) {
const px = halfCell + this.lastMoveCol * cellSize;
const py = halfCell + this.lastMoveRow * cellSize;
ctx.strokeStyle = LAST_MOVE_COLOR; // #E63946 红色
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(px, py, cellSize * 0.16, 0, Math.PI * 2);
ctx.stroke();
}
设计要点
- 红色空心圆 :
stroke()而非fill(),不遮挡棋子 - 半径
cellSize * 0.16:比棋子(0.42)小得多,是一个小标记 - 线宽2像素:足够醒目但不粗糙
- 条件绘制 :
lastMoveRow >= 0时才绘制
为什么用空心圆而非实心
实心圆(不推荐): 空心圆(本项目):
◯ ◯
◉ ← 实心遮挡棋子 ◉ ← 空心不遮挡
◯ ◯
空心圆画在棋子上面,但不遮挡棋子的渐变效果。
用户体验价值
在快节奏的对局中,玩家很容易忘记对手刚才下在哪里。最后一手标记解决了这个问题:
- 双人对战:双方都能看到最后一步
- AI对战:玩家能看清楚AI的落子位置
- 悔棋后:标记移动到上一步位置
标记的更新机制
typescript
// ChessBoardView通过@Prop接收
@Prop lastMoveRow: number = -1;
@Prop lastMoveCol: number = -1;
// 父组件更新
private refreshBoardData(): void {
this.lastMoveRow = this.engine.lastMove ? this.engine.lastMove.row : -1;
this.lastMoveCol = this.engine.lastMove ? this.engine.lastMove.col : -1;
}
当引擎的lastMove变化时,父组件更新@Prop,@Watch触发drawBoard()重绘,新的最后一手标记自动绘制。
悔棋后的标记
typescript
// GomokuEngine.undo()
if (this.history.length > 0) {
const prev = this.history[this.history.length - 1];
this.lastMove = new Move(prev.row, prev.col);
} else {
this.lastMove = null; // 没有历史时清除标记
}
悔棋后lastMove指向倒数第二步,标记自动移动。如果悔到空棋盘,lastMove为null,标记消失(lastMoveRow = -1)。
drawBoard中的绘制顺序
typescript
private drawBoard(): void {
// 1. 清空画布
ctx.clearRect(0, 0, size, size);
// 2. 绘制背景
ctx.fillRect(0, 0, size, size);
// 3. 绘制网格线
// 4. 绘制星位
// 5. 绘制棋子
// 6. 绘制最后一手标记 ← 最后绘制,在最上层
}
绘制顺序决定图层:后绘制的在上方。最后一手标记最后绘制,确保它在棋子之上。
总结
星位和最后一手标记看似是小细节,但对用户体验有重要影响:
- 星位:传统棋盘的视觉参照,增加专业感
- 最后一手标记:红色空心圆,醒目且不遮挡
- 绘制顺序:标记在棋子之上,确保可见
- 自动更新:通过@Prop数据驱动,无需手动管理
- 悔棋适配:标记自动跟随历史记录移动
这些细节让游戏从"能玩"变成"好玩"。