D3 强制布局 forceY 不映射到范围

Posted

技术标签:

【中文标题】D3 强制布局 forceY 不映射到范围【英文标题】:D3 force layout forceY does not map to range 【发布时间】:2021-02-17 18:58:34 【问题描述】:

我正在尝试使用forceY() 属性在 D3 强制布局中垂直排序我的节点,但我未能限制我的 y 轴的上限。我的画布高度为 200 像素,我有 50 个类别要订购并投影到高度为 1000 像素的平面上。我最初想看到顶部的 200px,而其余的节点可以在底部看不见。如果需要,我让用户在那里平移。

我使用let y_scale = d3.scaleLinear().domain([0, 50]).range([0, 1000]);d3.forceSimulation(nodes).force("y", d3.forceY().y(d => y_scale(d.category))) 结合使用,我认为这会使节点自然地绘制在 0 像素或更低,但事实并非如此。节点是根据类别排序的,但 0 类别不映射到 0px,而是说 -400px,这在顶部是看不见的。

有没有办法让forceY 真正映射到[0, 1000] 范围?

我知道我可以使用Math.max(0, d.y)之类的东西在svg上绘制时限制节点的y,但这只会使所有会在顶部拖尾的节点堆叠在0行。

【问题讨论】:

【参考方案1】:

对于分类变量,您应该改用d3.scaleBand()。这应该是定位怪异的主要原因:

const fruits = ["Apple", "Apple", "Pear", "Pear", "Orange", "Grape"];
const data = fruits.map(d => (
  x: 50,
  fruit: d
));
const colours = ["Red", "Green", "Orange", "Purple"];
const unique = arr => arr.filter((d, i) => arr.indexOf(d) === i);

const yscale = d3.scaleBand()
  .domain(unique(fruits))
  .range([50, 150]);

const colour = d3.scaleOrdinal()
  .domain(unique(fruits))
  .range(colours);

const circle = d3.select("body")
  .append("svg")
  .attr("height", 200)
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 4)
  .attr("cx", d => d.x)
  .attr("cy", d => yscale(d.fruit))
  .attr("fill", d => colour(d.fruit));


var graphLayout = d3.forceSimulation(data)
    .force("collide", d3.forceCollide().radius(5))
    .force("x", d3.forceX(50))
    .force("y", d3.forceY().y(d => yscale(d.fruit)))
    .on("tick", ticked);

function ticked() 
  circle
    .attr("cx", d => d.x)
    .attr("cy", d => d.y)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

【讨论】:

以上是关于D3 强制布局 forceY 不映射到范围的主要内容,如果未能解决你的问题,请参考以下文章

如何使 D3 中的标签和节点强制布局可点击以导航到 URL?

使用新数据重新渲染 D3 强制定向布局

无法将标签添加到 d3js 强制布局网络可视化

d3 以可预测的顺序强制布局节点

d3js 在地图上强制布局

在 D3 强制布局链接标记上缩放箭头