点击单元格边框区分区域功能存在封闭区域识别异常问题
问题修复:边框封闭区域识别异常
问题根源
原代码判断单元格连通性时,仅检查当前单元格的边框状态,未验证相邻单元格的对应边框是否同步存在。当边框状态不同步(如重复点击导致单边边框被取消)或遍历顺序导致先处理的区域未被完全隔离时,会出现B区域无法被识别为独立区域的情况。
修复方案
修改floodFill函数,在判断是否能连通到相邻单元格时,同时检查当前单元格的出边框和相邻单元格的入边框是否都不存在,确保两个单元格之间确实没有阻挡边框。
修复后的完整代码
JavaScript
const COLOR_PALETTE = ['#FDD', '#DFD', '#DDF', '#FFD', '#DBF', '#BFD', '#FDB', '#BDF']; const CLICK_DISTANCE_THRESHOLD = 5; const DATA = [ ['A', 'A', 'A', 'B'], ['C', 'C', 'A', 'B'], ['C', 'D', 'B', 'B'], ['C', 'D', 'D', 'D'] ]; const $grid = $('#grid'); $grid.append($('<tbody>') .append(DATA.map((row, rowIndex) => $('<tr>') .append(row.map((val, colIndex) => $('<td>') .text(val).data('coords', [rowIndex, colIndex])))))); $grid.on('click', 'td', onBorderClick); function onBorderClick(event) { const $target = $(this); const rect = this.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; // 判断点击的边框并切换状态 if (x < CLICK_DISTANCE_THRESHOLD) { $target.toggleClass('selected-left'); $target.prev().toggleClass('selected-right'); } else if (x > rect.width - CLICK_DISTANCE_THRESHOLD) { $target.toggleClass('selected-right'); $target.next().toggleClass('selected-left'); } else if (y < CLICK_DISTANCE_THRESHOLD) { $target.toggleClass('selected-top'); $target.parent().prev().children().eq($target.index()).toggleClass('selected-bottom'); } else if (y > rect.height - CLICK_DISTANCE_THRESHOLD) { $target.toggleClass('selected-bottom'); $target.parent().next().children().eq($target.index()).toggleClass('selected-top'); } distinguishRegions(); } function distinguishRegions() { // 清除之前的区域标记 $grid.find('td').css('background-color', '').removeData('region').removeAttr('data-region'); let regionId = 0; $grid.find('td').each(function() { const $cell = $(this); if (!$cell.data('region')) { regionId++; floodFill($cell, regionId); } }); } function floodFill($cell, regionId) { const queue = [$cell]; while (queue.length > 0) { const $current = queue.shift(); if ($current.length === 0 || $current.data('region')) continue; $current.data('region', regionId) .attr('data-region', regionId) .css('background-color', getRandomColor(regionId)); const [r, c] = $current.data('coords'); const neighbors = []; // 检查上方邻居:当前无顶部边框,且上方邻居无底部边框 const $top = getCell(r - 1, c); if ($top.length && !$current.hasClass('selected-top') && !$top.hasClass('selected-bottom')) { neighbors.push($top); } // 检查下方邻居:当前无底部边框,且下方邻居无顶部边框 const $bottom = getCell(r + 1, c); if ($bottom.length && !$current.hasClass('selected-bottom') && !$bottom.hasClass('selected-top')) { neighbors.push($bottom); } // 检查左方邻居:当前无左侧边框,且左方邻居无右侧边框 const $left = getCell(r, c - 1); if ($left.length && !$current.hasClass('selected-left') && !$left.hasClass('selected-right')) { neighbors.push($left); } // 检查右方邻居:当前无右侧边框,且右方邻居无左侧边框 const $right = getCell(r, c + 1); if ($right.length && !$current.hasClass('selected-right') && !$right.hasClass('selected-left')) { neighbors.push($right); } queue.push(...neighbors.filter($n => !$n.data('region'))); } } function getCell(rowIndex, colIndex) { return $grid.find(`tr:eq(${rowIndex})`).find(`td:eq(${colIndex})`); } function getRandomColor(index) { return COLOR_PALETTE[index % COLOR_PALETTE.length]; }
CSS
table { border: 2px solid grey; border-collapse: collapse; cursor: pointer; } td { width: 2rem; height: 2rem; border: 2px dotted grey; text-align: center; } .selected-left { border-left: 2px solid red; } .selected-right { border-right: 2px solid red; } .selected-top { border-top: 2px solid red; } .selected-bottom { border-bottom: 2px solid red; }
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <table id="grid"></table>
修改说明
- 在
floodFill函数中,针对每个方向的邻居增加了双向边框检查,确保两个单元格之间的边框状态完全同步,避免因单边边框异常导致的错误连通。 - 优化邻居判断逻辑,先验证邻居是否存在再检查边框状态,减少无效的空对象处理。
内容的提问来源于stack exchange,提问作者Mr. Polywhirl
相关产品推荐
相关产品推荐

