D3.js d3折线图标签重叠
我根据以下示例创建了一个折线图: 但是,在我的数据中,线标签(城市)最终会重叠,因为y轴上不同线的最终值通常很接近。我知道我需要比较每行的最后一个值,并在值相差12个单位或更少时向上或向下移动标签。我的想法是查看由这段代码编写的文本标签D3.js d3折线图标签重叠,d3.js,overlap,labels,linechart,D3.js,Overlap,Labels,Linechart,我根据以下示例创建了一个折线图: 但是,在我的数据中,线标签(城市)最终会重叠,因为y轴上不同线的最终值通常很接近。我知道我需要比较每行的最后一个值,并在值相差12个单位或更少时向上或向下移动标签。我的想法是查看由这段代码编写的文本标签 city.append("text") .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) .attr("transform", functi
city.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
如果y(d.value.temperature)值相差12或更小,则将这些值分开,直到它们之间至少有12个单位。你有没有想过如何做到这一点?这是我的第一个d3项目,语法仍然适合我 您最好一次传递所有标签——这也更符合d3的一般理念。然后可以编写如下代码:
svg.selectAll("text.label").data(data)
.enter()
.append("text")
.attr("transform", function(d, i) {
var currenty = y(d.value.temperature);
if(i > 0) {
var previousy = y(data[i-1].value.temperature),
if(currenty - previousy < 12) { currenty = previousy + 12; }
}
return "translate(" + x(d.value.date) + "," + currenty + ")";
})
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
svg.selectAll(“text.label”).data(数据)
.输入()
.append(“文本”)
.attr(“转换”,函数(d,i){
var电流y=y(d值、温度);
如果(i>0){
var previousy=y(数据[i-1]。值。温度),
如果(currenty y-previousy<12){currenty y=previousy+12;}
}
返回“translate(“+x(d.value.date)+”,“+currenty+”);
})
.attr(“x”,3)
.attr(“dy”,“.35em”)
.text(函数(d){返回d.name;});
这并不能解释上一个标签可能已被移动的事实。您可以显式获取上一个标签的位置,并根据该位置移动当前标签。代码几乎相同,只是需要保存对当前元素的引用(this
),以便以后可以访问它
所有这些都不会阻止标签与它们最终要标记的行之间可能存在相当大的距离。如果你需要移动每一个标签,最后一个标签会离你很远。更好的做法可能是单独创建图例,以便根据需要分隔标签和线条。考虑使用D3强制布局来放置标签。请参见此处的示例: 假设您有一个
数据
数组,其中包含具有值
属性的对象,以及比例y
:
// Create some nodes
const labels = data.map(d => {
return {
fx: 0,
targetY: y(d.value)
};
});
// Set up the force simulation
const force = d3.forceSimulation()
.nodes(labels)
.force('collide', d3.forceCollide(10))
.force('y', d3.forceY(d => d.targetY).strength(1))
.stop();
// Execute thte simulation
for (let i = 0; i < 300; i++) force.tick();
// Assign values to the appropriate marker
labels.sort((a, b) => a.y - b.y);
data.sort((a, b) => b.value - a.value);
data.forEach((d, i) => d.y = labels[i].y);
//创建一些节点
const labels=data.map(d=>{
返回{
外汇:0,
目标:y(d值)
};
});
//设置力模拟
const force=d3.forceSimulation()
.节点(标签)
.力(“碰撞”,d3.力碰撞(10))
力量('y',d3.力量(d=>d.targetY).力量(1))
.停止();
//执行thte模拟
对于(设i=0;i<300;i++)force.tick();
//将值指定给相应的标记
标签.排序((a,b)=>a.y-b.y);
data.sort((a,b)=>b.value-a.value);
data.forEach((d,i)=>d.y=标签[i].y);
现在,您的数据
数组将具有表示其最佳位置的y
属性
示例使用D34.0,请在此处阅读更多内容:除非我遗漏了什么,否则这只会检查标签上相邻行的重叠。例如,如果第1行的标签和第3行的标签重叠,则此例程将无法检测并修复该重叠。如果我误解了此代码的作用,请告诉我。谢谢您是正确的——但是,考虑上一个标签的位置而不是上一个数据点的位置时,会考虑到这一点。我的观点是,如果你在标签放置上有问题,几乎可以肯定最好有一个单独的图例。。。谢谢你让我放心,我不会错过一些简单的事情。