如何将战舰游戏HTML网格元素与JavaScript数组关联实现拖拽置船?
战舰游戏:拖拽船只同步网格数组实现方案
先搞定拖拽的基础配置
给每个可拖拽的船只DOM元素加上draggable="true",再用data-*属性存好船只的关键信息——比如长度data-length="3"、初始方向data-direction="horizontal"(水平/垂直)。然后监听dragstart事件,把这些信息存到拖拽数据里,顺便加个拖拽时的视觉反馈:
document.querySelectorAll('.ship').forEach(ship => { ship.draggable = true; ship.addEventListener('dragstart', (e) => { // 把船只数据转成JSON存在拖拽对象里 e.dataTransfer.setData('text/plain', JSON.stringify({ length: parseInt(ship.dataset.length), direction: ship.dataset.direction })); // 拖拽时给船只加个半透明样式,用户一眼能看出来 ship.classList.add('dragging'); }); // 拖拽结束后移除样式 ship.addEventListener('dragend', () => ship.classList.remove('dragging')); });
处理网格的放置逻辑
首先给player1Board里的每个单元格(假设是.cell类)设置data-row和data-col属性,比如<div class="cell" data-row="0" data-col="0"></div>,这样能快速拿到单元格的行列坐标。
然后监听单元格的dragover事件,阻止默认行为才能允许放置,顺便加个hover的视觉提示:
document.querySelectorAll('#player1Board .cell').forEach(cell => { cell.addEventListener('dragover', (e) => { e.preventDefault(); cell.classList.add('hovered'); }); cell.addEventListener('dragleave', () => cell.classList.remove('hovered')); });
接下来是核心的drop事件处理——拿到拖拽的船只信息,计算它要占据的所有单元格,同步更新数组和DOM:
document.querySelector('#player1Board').addEventListener('drop', (e) => { e.preventDefault(); const targetCell = e.target.closest('.cell'); // 防止点到单元格内部元素 if(!targetCell) return; targetCell.classList.remove('hovered'); // 取出之前存的船只数据 const shipData = JSON.parse(e.dataTransfer.getData('text/plain')); const dropRow = parseInt(targetCell.dataset.row); const dropCol = parseInt(targetCell.dataset.col); // 根据方向计算船只占据的所有单元格坐标 const shipCells = []; for(let i = 0; i < shipData.length; i++) { if(shipData.direction === 'horizontal') { shipCells.push({ row: dropRow, col: dropCol + i }); } else { shipCells.push({ row: dropRow + i, col: dropCol }); } } // 先检查放置是否合法:不超出网格,且没有重叠船只 const gridSize = player1.grid.length; const isValid = shipCells.every(cell => { return cell.row >= 0 && cell.row < gridSize && cell.col >= 0 && cell.col < gridSize && player1.grid[cell.row][cell.col] === 0; // 假设0代表空单元格,1代表有船 }); if(isValid) { // 同步更新数组和DOM shipCells.forEach(cell => { player1.grid[cell.row][cell.col] = 1; // 标记数组位置为有船 // 给对应单元格加样式,显示船只 const cellEl = document.querySelector(`#player1Board .cell[data-row="${cell.row}"][data-col="${cell.col}"]`); cellEl.classList.add('has-ship'); }); } else { // 提示用户放置无效 alert('这个位置放不了船哦,换个地方试试!'); } });
额外优化建议
- 加个方向切换:点击船只时切换
data-direction的值,同时更新船只的样式(比如水平变垂直),让用户能调整放置方向 - 拖拽预览:在
dragover时,提前把船只将要占据的单元格高亮出来,用户更清楚放置效果 - 支持重新拖拽:允许把已放置的船拖走,拖走时清空对应数组位置和DOM样式
内容的提问来源于stack exchange,提问作者Will Cione
相关产品推荐
相关产品推荐

