如何在 HTML 中实现国际象棋棋子的拖拽与格点吸附功能

浅强大大_2441

浅强大大_2441

2026-03-29

559人浏览

原创

如何在 HTML 中实现国际象棋棋子的拖拽与格点吸附功能

本文介绍如何使用现代 HTML/CSS/JavaScript 构建可拖拽、自动吸附到棋盘格的国际象棋界面,摒弃传统 布局,采用语义化 结构配合原生 drag-and-drop API 与坐标计算逻辑,实现流畅、可控的棋子移动体验。

本文介绍如何使用现代 html/css/javascript 构建可拖拽、自动吸附到棋盘格的国际象棋界面,摒弃传统 `

` 布局,采用语义化 `
` 结构配合原生 `drag-and-drop` API 与坐标计算逻辑,实现流畅、可控的棋子移动体验。

构建一个支持拖拽与格点吸附的国际象棋界面,关键在于结构灵活性、事件精准控制和视觉反馈闭环。使用

实现棋盘虽直观,但会严重限制元素定位、拖拽目标判定及 CSS 变换能力;而基于
的网格布局(如 Flexbox 或 CSS Grid)可自由设置 position: relative/absolute、启用 draggable="true",并结合 getBoundingClientRect() 精确计算落点。

以下是一个最小可行实现(含完整 HTML + 内联 CSS/JS),聚焦「拖拽 → 悬停高亮 → 松开后吸附至最近棋盘格」核心流程:

@@######@@

✅ 关键要点说明:

HTML Text Extract
HTML Text Extract

从HTML页面(URL、文件或标准输入)中提取主体内容文本,去除导航、页脚、广告和模板文本。可直接传递给readability_check或其他文本工具。

下载
  • 结构优势:
    构成响应式网格,每格独立可交互,避免
的 DOM 层级嵌套与样式限制;
  • 拖拽启用:通过 draggable="true"(或 CSS user-drag: element)激活原生拖拽,无需第三方库;
  • 吸附逻辑:利用 clientX/clientY 与棋盘 getBoundingClientRect() 计算相对坐标,除以单格尺寸后 Math.round() 实现像素级吸附;
  • 无障碍友好:保留 role="grid" 和 aria-label 可进一步增强可访问性(生产环境建议补充);
  • 扩展提示:后续加入合法走法校验时,可在 drop 事件中调用 isValidMove(draggedPiece, fromRow, fromCol, toRow, toCol) 进行拦截。
  • ⚠️ 注意事项:

    • 原生 drag-and-drop 在移动端支持有限,如需全平台兼容,建议搭配 interact.js 或自定义 touchstart/move/end 手势;
    • 当前示例未持久化棋子状态,真实项目应维护一个二维数组 boardState[8][8] 并同步 UI;
    • 若使用图片棋子(如提问者 GitHub 仓库中的 PNG),请将
      替换为 @@##@@,并确保 img 元素同样绑定 dragstart 事件。

      掌握此模式后,你已具备构建专业级交互棋盘的核心能力——下一步可无缝集成 FEN 解析、走法高亮、悔棋栈与 UCI 引擎通信。

    
    
      <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实战笔记:立即使用
    在学习笔记中,你将探索 前端 的入门与实战技巧!

    相关文章

    HTML速学教程(入门课程)
    HTML速学教程(入门课程)

    HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

    下载

    相关标签:

    html

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    相关专题

    更多
    html版权符号
    html版权符号

    html版权符号是“©”,可以在html源文件中直接输入或者从word中复制粘贴过来,php中文网还为大家带来html的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

    2023.06.14

    6735

    7

    html在线编辑器
    html在线编辑器

    html在线编辑器是用于在线编辑的工具,编辑的内容是基于HTML的文档。它经常被应用于留言板留言、论坛发贴、Blog编写日志或等需要用户输入普通HTML的地方,是Web应用的常用模块之一。php中文网为大家带来了html在线编辑器的相关教程、以及相关文章等内容,供大家免费下载使用。

    2023.06.21

    3732

    4

    html网页制作
    html网页制作

    html网页制作是指使用超文本标记语言来设计和创建网页的过程,html是一种标记语言,它使用标记来描述文档结构和语义,并定义了网页中的各种元素和内容的呈现方式。本专题为大家提供html网页制作的相关的文章、下载、课程内容,供大家免费下载体验。

    2023.07.31

    3410

    5

    html空格
    html空格

    html空格是一种用于在网页中添加间隔和对齐文本的特殊字符,被用于在网页中插入额外的空间,以改变元素之间的排列和对齐方式。本专题为大家提供html空格的相关的文章、下载、课程内容,供大家免费下载体验。

    2023.08.01

    3439

    5

    html是什么
    html是什么

    HTML是一种标准标记语言,用于创建和呈现网页的结构和内容,是互联网发展的基石,为网页开发提供了丰富的功能和灵活性。本专题为大家提供html相关的各种文章、以及下载和课程。

    2023.08.11

    5059

    6

    html字体大小怎么设置
    html字体大小怎么设置

    在网页设计中,字体大小的选择是至关重要的。合理的字体大小不仅可以提升网页的可读性,还能够影响用户对网页整体布局的感知。php中文网将介绍一些常用的方法和技巧,帮助您在HTML中设置合适的字体大小。

    2023.08.11

    3361

    3

    html转txt
    html转txt

    html转txt的方法有使用文本编辑器、使用在线转换工具和使用Python编程。本专题为大家提供html转txt相关的文章、下载、课程内容,供大家免费下载体验。

    2023.08.31

    3189

    3

    html文本框代码怎么写
    html文本框代码怎么写

    html文本框代码:1、单行文本框【<input type="text" style="height:..;width:..;" />】;2、多行文本框【textarea style=";height:;"></textare】。

    2023.09.01

    2768

    6

    HTML嵌入CSS样式的方法
    HTML嵌入CSS样式的方法

    HTML嵌入CSS样式的方法有内联样式、内部样式表和外部样式表。本专题为大家提供CSS样式相关的文章、下载、课程内容,供大家免费下载体验。

    2023.09.20

    2888

    5

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    GDB 17.2 官方文档集合
    GDB 17.2 官方文档集合

    共0课时 | 0人学习

    Bootstrap 入门安装配置
    Bootstrap 入门安装配置

    共0课时 | 0人学习

    38+ PhpStorm 提示和技巧
    38+ PhpStorm 提示和技巧

    共1课时 | 228人学习