
本文介绍如何使用现代 HTML/CSS/JavaScript 构建可拖拽、自动吸附到棋盘格的国际象棋界面,摒弃传统 布局,采用语义化 结构配合原生 drag-and-drop API 与坐标计算逻辑,实现流畅、可控的棋子移动体验。
本文介绍如何使用现代 html/css/javascript 构建可拖拽、自动吸附到棋盘格的国际象棋界面,摒弃传统 `
<meta charset="UTF-8"><title>可拖拽国际象棋棋盘</title><style>
.chessboard {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
width: 480px; height: 480px;
margin: 20px auto;
border: 2px solid #333;
box-shadow: 0 0 12px rgba(0,0,0,0.2);
}
.square {
display: flex;
align-items: center;
justify-content: center;
font-size: 36px;
user-select: none;
transition: background-color 0.15s;
}
.square.light { background-color: #f0d9b5; }
.square.dark { background-color: #b58863; }
.square.drag-over { background-color: #a0d8f1 !important; }
.piece {
cursor: grab;
user-select: none;
z-index: 10;
/* 启用拖拽 */
-webkit-user-drag: element;
-khtml-user-drag: element;
-moz-user-drag: element;
-o-user-drag: element;
user-drag: element;
}
/* 隐藏默认拖拽虚线框 */
::-webkit-dnd-feedback { opacity: 0; }
</style><div class="chessboard" id="board"></div>
<script>
const board = document.getElementById('board');
const pieceSymbols = {
'r': '♜', 'n': '♞', 'b': '♝', 'q': '♛', 'k': '♚', 'p': '♟',
'R': '♖', 'N': '♘', 'B': '♗', 'Q': '♕', 'K': '♔', 'P': '♙'
};
// 初始化棋盘(8×8)
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const square = document.createElement('div');
square.className = `square ${(row + col) % 2 === 0 ? 'light' : 'dark'}`;
square.dataset.row = row;
square.dataset.col = col;
// 初始摆放(简化:仅黑白两排兵 + 底排)
if (row === 1) square.innerHTML = pieceSymbols['p']; // 黑兵
if (row === 6) square.innerHTML = pieceSymbols['P']; // 白兵
if (row === 0) {
const backRank = ['r','n','b','q','k','b','n','r'];
square.innerHTML = pieceSymbols[backRank[col]];
}
if (row === 7) {
const backRank = ['R','N','B','Q','K','B','N','R'];
square.innerHTML = pieceSymbols[backRank[col]];
}
board.appendChild(square);
}
}
// 拖拽逻辑
let draggedPiece = null;
let dragStartX = 0;
let dragStartY = 0;
document.addEventListener('dragstart', e => {
if (e.target.classList.contains('piece')) {
draggedPiece = e.target;
e.dataTransfer.setData('text/plain', '');
// 记录初始偏移(用于吸附计算)
const rect = e.target.getBoundingClientRect();
dragStartX = e.clientX - rect.left;
dragStartY = e.clientY - rect.top;
e.target.style.opacity = '0.7';
e.target.style.zIndex = '20';
}
});
document.addEventListener('dragend', () => {
if (draggedPiece) {
draggedPiece.style.opacity = '';
draggedPiece.style.zIndex = '';
draggedPiece = null;
}
});
document.addEventListener('dragover', e => {
e.preventDefault(); // 必须阻止默认行为才能触发 drop
});
document.addEventListener('drop', e => {
e.preventDefault();
if (!draggedPiece) return;
const boardRect = board.getBoundingClientRect();
const x = e.clientX - boardRect.left;
const y = e.clientY - boardRect.top;
const cellSize = boardRect.width / 8;
// 计算最近格子索引(四舍五入取整)
const col = Math.round(x / cellSize) - 1;
const row = Math.round(y / cellSize) - 1;
// 边界校验
if (row < 0 || row > 7 || col < 0 || col > 7) return;
const targetSquare = board.children[row * 8 + col];
// 清空目标格(简易逻辑,暂不校验吃子)
targetSquare.innerHTML = '';
// 将棋子追加到目标格(自动居中)
targetSquare.appendChild(draggedPiece);
});
// 可选:悬停高亮(增强 UX)
document.addEventListener('dragenter', e => {
if (e.target.classList.contains('square')) {
e.target.classList.add('drag-over');
}
});
document.addEventListener('dragleave', e => {
if (e.target.classList.contains('square')) {
e.target.classList.remove('drag-over');
}
});
</script>
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











