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

解析Mike Bostok的D3方格网格代码:单元格定位与取模运算疑问

解析D3方格网格的单元格定位代码及通用方法

先来看你提到的这段设置单元格位置的代码,我会一步步拆解它的逻辑,重点讲清楚取模运算符的作用:

.attr("x", function(i) { 
  var x0 = Math.floor(i / 100) % 10, 
      x1 = Math.floor(i % 10); 
  return groupSpacing * x0 + (cellSpacing + cellSize) * (x1 + x0 * 10); 
}) 
.attr("y", function(i) { 
  var y0 = Math.floor(i / 1000), 
      y1 = Math.floor(i % 100 / 10); 
  return groupSpacing * y0 + (cellSpacing + cellSize) * (y1 + y0 * 10); 
})

一、代码逻辑拆解(以10x10分组的100x100总网格为例)

这个实例里的网格是两层结构:大网格由10x10个小组组成,每个小组内部又是10x10个单元格,总共有10000个单元格(对应索引i从0到9999)。

1. x坐标计算

  • x0 = Math.floor(i / 100) % 10:
    • Math.floor(i / 100):把索引i按每100个单元格划分为一个小组(每个小组10x10=100个单元格),得到小组的序号(0-99)。
    • % 10:取模运算符在这里的作用是截取小组序号的“列位置”——因为大网格是10列小组,所以小组序号除以10的余数就是该小组在大网格里的x方向位置(0-9)。
  • x1 = Math.floor(i % 10):
    • i % 10:取模得到i在小组内的列索引(0-9),因为每个小组有10列单元格,余数直接对应列位置。
  • 最终x坐标:
    groupSpacing * x0是小组之间的横向间距总和,(cellSpacing + cellSize) * (x1 + x0 * 10)是所有前面小组的单元格宽度(含间距)加上当前小组内的单元格偏移,两者相加就是单元格的最终横向位置。

2. y坐标计算

  • y0 = Math.floor(i / 1000):
    每1000个单元格对应大网格的一行小组(10个小组×每个小组100个单元格=1000),所以直接除以1000取整得到小组在大网格里的y方向位置(0-9)。
  • y1 = Math.floor(i % 100 / 10):
    • i % 100:取模得到i在小组内的索引(0-99),
    • 再除以10取整,就得到单元格在小组内的行位置(0-9)——相当于把小组内的100个单元格按10行划分。
  • 最终y坐标:
    和x逻辑一致,groupSpacing * y0是小组之间的纵向间距,(cellSpacing + cellSize) * (y1 + y0 * 10)是前面所有小组的单元格高度(含间距)加上当前小组内的单元格偏移,总和就是纵向位置。

取模运算符的核心作用

取模%在这里是从连续的索引中提取层级化的位置信息:通过取模可以把一个连续的数字(索引i)拆分成“大组位置”“小组内位置”等不同维度的信息,相当于把一维的索引映射到二维的网格坐标上。

二、网格单元格定位的通用方法(适配按数据比例设置位置的需求)

如果你需要根据数据中的变量比例来设置单元格位置,可以参考以下思路:

1. 单层网格(无分组)

如果你的网格是单层结构,直接用D3的比例尺来映射数据到坐标:

  • 数值型比例:用d3.scaleLinear(),把数据的数值范围映射到容器的宽度/高度范围。比如:
    const xScale = d3.scaleLinear()
      .domain([0, d3.max(data, d => d.value)]) // 数据的数值范围
      .range([0, containerWidth]); // 容器的宽度范围
    
    // 单元格x坐标
    .attr("x", d => xScale(d.value))
    
  • 分类型比例:如果是按分类变量占比分配位置,用d3.scaleBand(),可以自定义每个分类的带宽比例,或者让D3自动分配:
    const xScale = d3.scaleBand()
      .domain(data.map(d => d.category)) // 分类列表
      .range([0, containerWidth])
      .padding(0.1); // 单元格间距比例
    
    .attr("x", d => xScale(d.category))
    

2. 多层级网格(有分组)

如果需要分组,先确定组的位置(按比例分配组的大小),再处理组内单元格:

  1. 先为组创建比例尺,计算每个组的起始位置和大小;
  2. 在组内再创建子比例尺,根据组内数据的比例计算单元格位置;
  3. 最终坐标 = 组的起始位置 + 组内单元格的偏移位置。

3. 通用步骤总结

  • 明确你的网格结构(单层/多层、固定大小/比例分配);
  • 根据数据类型选择合适的D3比例尺(线性、带状、序数等);
  • 绑定数据后,通过比例尺将数据值映射为具体的x/y坐标;
  • 如果需要间距,在计算坐标时加入间距的偏移量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:29:40