创建可点击交点的响应式Gomoku(五子棋)棋盘
五子棋棋盘适配与简化实现方案
问题1:让网格在所有屏幕尺寸下正常显示(最小宽度275px)
核心思路是强制棋盘容器保持正方形比例,同时限制宽高范围,再用Grid布局均分单元格:
给棋盘父容器设置基础样式
.gomoku-board { /* 限制宽度范围 */ min-width: 275px; max-width: 90vw; /* 大屏幕下不撑满,留边距 */ /* 强制正方形 */ aspect-ratio: 1 / 1; margin: 0 auto; /* 水平居中 */ /* Grid布局均分单元格 */ display: grid; grid-template-columns: repeat(15, 1fr); /* 15路棋盘,按需修改路数 */ grid-template-rows: repeat(15, 1fr); position: relative; padding: 0; border: 2px solid #333; /* 棋盘外框 */ }单元格(点击区域)样式
单元格只需要作为点击容器,交点和棋盘线用父容器伪元素实现,避免DOM冗余:.cell { position: relative; cursor: pointer; }用伪元素绘制棋盘网格线(替代单元格边框,避免重复线条)
.gomoku-board::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; /* 生成横竖网格线 */ background-image: linear-gradient(to right, #333 1px, transparent 1px), linear-gradient(to bottom, #333 1px, transparent 1px); /* 线间距:15路棋盘的话,是总宽高除以14(间隔数) */ background-size: calc(100% / 14) calc(100% / 14); background-position: center; pointer-events: none; /* 不干扰单元格点击 */ }点击后显示棋子(Vue中通过绑定数据控制)
比如给单元格添加v-if显示棋子:<div class="cell" @click="handleClick(row, col)"> <div class="piece" v-if="board[row][col]" :class="board[row][col]"></div> </div>棋子样式:
.piece { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 70%; height: 70%; border-radius: 50%; } .piece.black { background-color: #000; } .piece.white { background-color: #fff; border: 1px solid #000; }
这样设置后,棋盘会自动适配屏幕:大屏幕下最多占90%宽度,小屏幕下保持275px宽,始终是正方形,单元格也会跟着等比例缩放。
问题2:更简单的实现方案
推荐用Canvas绘制棋盘,DOM结构极简,适配更轻松,性能也更好(尤其多路数棋盘):
Vue中定义Canvas元素
<canvas ref="gomokuCanvas" @click="handleCanvasClick"></canvas>Canvas样式设置(同样保持正方形比例)
canvas { min-width: 275px; max-width: 90vw; aspect-ratio: 1 / 1; display: block; margin: 0 auto; border: 2px solid #333; }初始化绘制棋盘
在Vue的mounted钩子中获取Canvas上下文,绘制网格线:const canvas = this.$refs.gomokuCanvas; const ctx = canvas.getContext('2d'); // 设置Canvas实际分辨率(避免模糊) const size = Math.max(canvas.offsetWidth, 275); canvas.width = size; canvas.height = size; const gridCount = 15; // 15路棋盘 const gridSize = size / (gridCount - 1); // 绘制网格线 ctx.strokeStyle = '#333'; ctx.lineWidth = 1; for (let i = 0; i < gridCount; i++) { // 横线 ctx.beginPath(); ctx.moveTo(0, i * gridSize); ctx.lineTo(size, i * gridSize); ctx.stroke(); // 竖线 ctx.beginPath(); ctx.moveTo(i * gridSize, 0); ctx.lineTo(i * gridSize, size); ctx.stroke(); }处理点击事件,计算交点并绘制棋子
handleCanvasClick(e) { const canvas = this.$refs.gomokuCanvas; const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const gridSize = canvas.width / 14; // 15路的间隔 // 计算最近的网格交点 const col = Math.round(x / gridSize); const row = Math.round(y / gridSize); // 绘制棋子 const ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.arc(col * gridSize, row * gridSize, gridSize * 0.35, 0, Math.PI * 2); ctx.fillStyle = this.currentPlayer === 'black' ? '#000' : '#fff'; ctx.fill(); ctx.strokeStyle = '#000'; ctx.stroke(); // 切换玩家等逻辑... }
这种方案不需要生成大量的单元格DOM,只用一个Canvas就能完成棋盘绘制和交互,适配逻辑更简单,代码量也更少。
内容的提问来源于stack exchange,提问作者eligolf
相关产品推荐
相关产品推荐

