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

D3.js中点击矩阵像素时更新相邻元素的实现咨询

D3.js中点击矩阵像素时更新相邻元素的实现咨询

嘿,我看你已经用D3搭好了一个基础的像素矩阵,目前点击只能删除当前元素,但想要实现点击后更新相邻像素的功能对吧?我来帮你梳理下怎么调整代码,实现你想要的效果~

首先,咱们先分析下原代码的小问题:

  • 原点击事件里只处理了当前元素的删除,但没用到数据里自带的x/y坐标信息,没法定位相邻元素
  • 直接删除DOM元素不是你想要的目标,咱们应该用数据驱动的方式来更新视图,这样既能轻松获取所有像素的状态,也能方便地更新元素样式

接下来是具体的改进步骤和代码:

1. 简化坐标计算,给像素添加初始颜色

原代码里通过索引计算transform的方式有点绕,咱们可以直接用数据里的x和y来定位,同时根据value值给像素设置初始填充色:

.attr("transform", d => `translate(${d.x * entry_side_length}, ${d.y * entry_side_length})`)
.attr("fill", d => d.value ? "#000" : "#fff") // 1=黑色,0=白色

2. 编写点击事件逻辑,更新相邻像素

咱们需要在点击事件里:

  • 获取当前点击像素的坐标
  • 找出上下左右四个邻居(注意边界判断,避免超出矩阵范围)
  • 更新邻居的数据状态
  • 重新渲染视图,让样式和数据同步

修改后的点击事件函数如下:

function handlePixelClick(d) {
  // 拿到当前点击像素的坐标
  const { x, y } = d;
  // 定义四个邻居的坐标偏移
  const neighborOffsets = [
    { dx: -1, dy: 0 }, // 左
    { dx: 1, dy: 0 },  // 右
    { dx: 0, dy: -1 }, // 上
    { dx: 0, dy: 1 }   // 下
  ];

  // 遍历所有邻居,更新它们的value值(这里用取反演示,你可以换成自己的计算逻辑)
  neighborOffsets.forEach(offset => {
    const neighborX = x + offset.dx;
    const neighborY = y + offset.dy;
    // 检查邻居坐标是否在矩阵范围内
    if (neighborX >= 0 && neighborX < cols && neighborY >= 0 && neighborY < rows) {
      // 找到对应的数据项
      const neighborData = data.find(item => item.x === neighborX && item.y === neighborY);
      if (neighborData) {
        neighborData.value = 1 - neighborData.value; // 翻转颜色值
      }
    }
  });

  // 重新绑定数据,更新所有像素的颜色
  canvas.selectAll("rect")
    .data(data)
    .attr("fill", d => d.value ? "#000" : "#fff");
}

然后把原有的on("click", delete_element)替换成on("click", handlePixelClick)就可以了。

3. 获取所有像素的状态

现在所有像素的状态都存在data数组里,你随时可以遍历这个数组来获取每个像素的x/y/value,做你需要的各种计算,比如统计黑色像素数量、判断特定区域的状态等等。

完整修改后的代码

chart = {
  const cols = 5;
  const rows = 5;
  const entry_side_length = 20;
  const matrix_width = cols * entry_side_length;
  const matrix_height = rows * entry_side_length;

  // 生成随机0/1矩阵数据
  let data = [];
  for (let i = 0; i < cols; i++) {
    for (let j = 0; j < rows; j++) { // 修正原代码内层循环的小bug,cols改为rows
      data.push({ x: i, y: j, value: Math.round(Math.random()) });
    }
  }

  let canvas = d3.create("svg")
    .attr("width", `${matrix_width}px`)
    .attr("height", `${matrix_height}px`);

  canvas.append("g").selectAll("rect")
    .data(data)
    .join("rect")
    .attr("width", `${entry_side_length}px`)
    .attr("height", `${entry_side_length}px`)
    .attr("transform", d => `translate(${d.x * entry_side_length}, ${d.y * entry_side_length})`)
    .attr("fill", d => d.value ? "#000" : "#fff")
    .attr("stroke", "#ccc") // 加个边框区分像素
    .on("click", handlePixelClick);

  function handlePixelClick(d) {
    const { x, y } = d;
    const neighborOffsets = [
      { dx: -1, dy: 0 },
      { dx: 1, dy: 0 },
      { dx: 0, dy: -1 },
      { dx: 0, dy: 1 }
    ];

    neighborOffsets.forEach(offset => {
      const nx = x + offset.dx;
      const ny = y + offset.dy;
      if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
        const neighbor = data.find(item => item.x === nx && item.y === ny);
        if (neighbor) {
          neighbor.value = 1 - neighbor.value;
        }
      }
    });

    // 更新视图
    canvas.selectAll("rect")
      .data(data)
      .attr("fill", d => d.value ? "#000" : "#fff");
  }

  return Object.assign(canvas.node())
}

小提示

我还帮你修正了原代码里的一个小bug:生成数据时内层循环的条件是j < cols,应该改成j < rows,这样才会生成正确的5x5矩阵~

这样修改后,点击任意像素,它的四个相邻像素就会翻转颜色,而且你可以随时通过data数组获取所有像素的状态,方便后续扩展更多功能。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 08:15:29