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 不映射到范围的主要内容,如果未能解决你的问题,请参考以下文章