You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为像素网格实现JavaScript版FloodFill函数?

像素网格绘图工具的FloodFill(油漆桶)实现方案

核心问题分析

你之前尝试DFS/BFS失败,大概率是颜色判断逻辑不严谨导致的:浏览器返回的元素颜色格式不统一(比如rgb(0,0,0)、#000、black都是黑色,但字符串完全不同),或者没有处理边界/重复遍历的问题。下面是可直接复用的实现方案及原理。


实现代码

1. 颜色标准化工具函数

先统一颜色格式为十六进制,解决不同格式颜色无法匹配的问题:

function normalizeColor(color) {
  // 创建临时元素获取计算后的标准颜色
  const tempEl = document.createElement('div');
  tempEl.style.color = color;
  document.body.appendChild(tempEl);
  const computedColor = getComputedStyle(tempEl).color;
  document.body.removeChild(tempEl);

  // 将RGB/RGBA转为十六进制
  if (computedColor.startsWith('rgb')) {
    const values = computedColor.match(/\d+/g).map(Number);
    return '#' + values.slice(0,3).map(v => v.toString(16).padStart(2, '0')).join('');
  }
  return computedColor.toLowerCase();
}

2. BFS版FloodFill核心函数

推荐用BFS(队列实现),避免DFS递归导致的栈溢出问题:

function floodFill(startPixel, targetColor) {
  const originalColor = normalizeColor(startPixel.style.backgroundColor);
  const normalizedTarget = normalizeColor(targetColor);

  // 目标颜色与原始颜色一致时直接返回,避免无效操作
  if (originalColor === normalizedTarget) return;

  const queue = [startPixel];
  const gridContainer = startPixel.parentElement;
  const pixelSize = startPixel.offsetWidth;
  // 计算网格的行列数
  const cols = Math.floor(gridContainer.offsetWidth / pixelSize);
  const rows = Math.floor(gridContainer.offsetHeight / pixelSize);

  // 获取像素在网格中的索引
  const getPixelIndex = (pixel) => Array.from(gridContainer.children).indexOf(pixel);

  while (queue.length > 0) {
    const currentPixel = queue.shift();
    // 二次检查颜色,防止已被其他路径修改
    const currentColor = normalizeColor(currentPixel.style.backgroundColor);
    if (currentColor !== originalColor) continue;

    // 设置目标颜色
    currentPixel.style.backgroundColor = normalizedTarget;

    const pixelIndex = getPixelIndex(currentPixel);
    const currentRow = Math.floor(pixelIndex / cols);
    const currentCol = pixelIndex % cols;

    // 遍历上下左右四个方向的相邻像素
    const directions = [
      { row: currentRow - 1, col: currentCol }, // 上
      { row: currentRow + 1, col: currentCol }, // 下
      { row: currentRow, col: currentCol - 1 }, // 左
      { row: currentRow, col: currentCol + 1 }  // 右
    ];

    for (const dir of directions) {
      // 边界校验:确保像素在网格范围内
      if (dir.row >= 0 && dir.row < rows && dir.col >= 0 && dir.col < cols) {
        const neighborIndex = dir.row * cols + dir.col;
        const neighborPixel = gridContainer.children[neighborIndex];
        const neighborColor = normalizeColor(neighborPixel.style.backgroundColor);
        // 颜色匹配则加入队列
        if (neighborColor === originalColor) {
          queue.push(neighborPixel);
        }
      }
    }
  }
}

3. 绑定点击事件(示例)

给每个像素绑定点击触发油漆桶功能:

document.querySelectorAll('.pixel').forEach(pixel => {
  pixel.addEventListener('click', () => {
    // 这里可以替换为你的颜色选择器返回的目标颜色
    floodFill(pixel, '#ff0000');
  });
});

原理说明

  1. 颜色标准化:通过临时元素的计算样式,把所有颜色转为小写十六进制格式,确保同色不同字符串的情况能正确匹配。
  2. BFS遍历逻辑:
    • 以点击的像素为种子,加入队列。
    • 逐个取出队列中的像素,替换颜色后,检查上下左右四个相邻像素。
    • 符合颜色条件且在网格边界内的像素,加入队列继续处理。
  3. 避免重复/无效操作:
    • 初始判断目标颜色与原始颜色是否一致,直接跳过。
    • 从队列取出像素时二次检查颜色,防止同一像素被多次加入队列后重复处理。
  4. 边界控制:通过计算网格行列数,确保只遍历有效范围内的像素,避免访问不存在的DOM元素。

内容的提问来源于stack exchange,提问作者Mohammad Kabalan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 14:23:09