解析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. 多层级网格(有分组)
如果需要分组,先确定组的位置(按比例分配组的大小),再处理组内单元格:
- 先为组创建比例尺,计算每个组的起始位置和大小;
- 在组内再创建子比例尺,根据组内数据的比例计算单元格位置;
- 最终坐标 = 组的起始位置 + 组内单元格的偏移位置。
3. 通用步骤总结
- 明确你的网格结构(单层/多层、固定大小/比例分配);
- 根据数据类型选择合适的D3比例尺(线性、带状、序数等);
- 绑定数据后,通过比例尺将数据值映射为具体的x/y坐标;
- 如果需要间距,在计算坐标时加入间距的偏移量。
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

