
本文详解如何为基于 css grid 的像素画板实现可靠的 flood fill 功能:通过定位点击元素、获取原始颜色、递归遍历四邻域并填充,避免越界与重复操作,兼顾可读性与实际可用性。
本文详解如何为基于 css grid 的像素画板实现可靠的 flood fill 功能:通过定位点击元素、获取原始颜色、递归遍历四邻域并填充,避免越界与重复操作,兼顾可读性与实际可用性。
Flood Fill(泛洪填充)是像素级绘图工具的核心功能之一,常用于“油漆桶”工具。其本质是从用户点击的起始像素出发,将所有与之连通、且颜色相同的相邻区域,统一替换为目标填充色。在本例中,我们使用纯 JavaScript + 原生 DOM 操作实现,无需 Canvas,直接操作 <div class="grid-item"> 的 style.backgroundColor。
✅ 核心实现逻辑
关键在于准确定位、安全边界判断、颜色比对与递归扩散:
- 定位起点:利用 Array.from(parentNode.children).indexOf(clickedItem) 获取点击格子在整行整列中的线性索引(0-based);
- 获取网格维度:根据当前缩放级别(如 8×8、16×16)计算每行格子数 itemsPerRow;
- 捕获原始颜色:读取 clickedItem.style.backgroundColor —— 注意必须是内联样式(本例中所有格子均通过 JS 设置 style.backgroundColor,因此可靠);
- 递归填充:对上下左右四个正交方向进行 DFS 遍历,仅当目标格子存在、且背景色严格等于原始色时才填充并继续递归。
以下是完整、已验证的 floodFill 函数(含健壮性优化):
function isValidIndex(index, length) {
return index >= 0 && index < length;
}
function floodFill(element) {
if (!element || !element.classList.contains('grid-item')) return;
const clickedItem = element;
const selectedValue = myRange[indexRangeInput.value]; // 当前网格边长(如 16)
const itemsPerRow = selectedValue;
const gridItems = Array.from(clickedItem.parentNode.children); // 转为数组便于索引
const index = gridItems.indexOf(clickedItem);
const originalColor = clickedItem.style.backgroundColor || 'white'; // 兜底 white
// 防止填充自身颜色与目标色相同 → 无限递归或无效果
if (colorsAreSame(originalColor, selectedColor)) return;
// 闭包递归函数(避免全局污染)
function fill(currentIndex) {
if (!isValidIndex(currentIndex, gridItems.length)) return;
const currentItem = gridItems[currentIndex];
// 严格颜色匹配(注意:rgb(0,0,0) ≠ #000,需保持格式一致)
if (currentItem.style.backgroundColor === originalColor) {
currentItem.style.backgroundColor = selectedColor;
// 四方向递归(不包含对角线,符合标准 Flood Fill)
fill(currentIndex - itemsPerRow); // 上
fill(currentIndex + itemsPerRow); // 下
fill(currentIndex - 1); // 左(需检查是否为行首)
fill(currentIndex + 1); // 右(需检查是否为行尾)
}
}
// 启动填充
fill(index);
}⚠️ 重要注意事项
颜色格式一致性:确保所有格子的 backgroundColor 均以 rgb(r, g, b) 格式设置(如 'rgb(255, 0, 0)'),否则 === 比较会失败。本例中 createGrid() 初始化为 'white',而 selectedColor 为 rgb(...),因此建议初始化时也统一为 rgb(255,255,255)。
-
边界防错:fill(currentIndex ± 1) 需隐式判断是否跨行 —— 例如从第 0 列左移会进入上一行末尾,导致误填。更严谨的做法是增加列边界检查:
const row = Math.floor(currentIndex / itemsPerRow); const col = currentIndex % itemsPerRow; if (col > 0) fill(currentIndex - 1); // 左:非首列 if (col < itemsPerRow - 1) fill(currentIndex + 1); // 右:非末列
-
性能与栈深度:递归 DFS 在大型网格(如 64×64)中可能触发浏览器栈溢出。生产环境推荐改用 迭代 BFS(队列) 或带栈限制的 DFS。示例 BFS 替代方案:
function floodFillBFS(element) { const queue = [gridItems.indexOf(element)]; const original = element.style.backgroundColor || 'white'; if (colorsAreSame(original, selectedColor)) return; while (queue.length > 0) { const idx = queue.shift(); const item = gridItems[idx]; if (item.style.backgroundColor !== original) continue; item.style.backgroundColor = selectedColor; const row = Math.floor(idx / itemsPerRow); const col = idx % itemsPerRow; if (row > 0) queue.push(idx - itemsPerRow); // 上 if (row < itemsPerRow - 1) queue.push(idx + itemsPerRow); // 下 if (col > 0) queue.push(idx - 1); // 左 if (col < itemsPerRow - 1) queue.push(idx + 1); // 右 } }
✅ 总结
Flood Fill 并非黑盒算法,而是对“连通区域”概念的直观编码。本文实现紧扣 DOM 结构特性,以索引运算替代坐标转换,用原生 JS 完成高效填充。只要保证颜色格式统一、边界判断严谨、递归/迭代策略得当,即可稳定集成到任意网格画板中。建议后续扩展支持透明度(RGBA)、抗锯齿预览或撤销栈,进一步提升专业体验。

















