javascript - 如何使用 d3.force 对散点图进行动态、非重叠标记
问题描述
编辑:是否可以将 d3.force 导入我的反应组件?如果没有,那么我可能会无缘无故地为此苦苦挣扎。
从谷歌搜索问题(如何在不重叠的情况下将标签添加到散点图),答案似乎是 d3.force (至少这是答案之一),这是我想尝试包装的答案转头。
我创建了一个非常小、简单的示例,其中包含约 15 个圆圈及其相应的文本标签,这些标签当前位于标记右侧 6 像素和标记下方 3 像素。结果是重叠标签名称的混乱图。
const myData = [
{x:25, y:30, name:"tommy"},
{x:8, y:12, name:"joey"},
{x:92, y:107, name:"nicky"},
{x:85, y:50, name:"peter"},
{x:65, y:80, name:"mickie"},
{x:65, y:80, name:"gregie"},
{x:54, y:6, name:"tammie"},
{x:102, y:42, name:"benny"},
{x:66, y:45, name:"dennie"},
{x:81, y:44, name:"jerryi"},
{x:127, y:36, name:"garrie"},
{x:62, y:30, name:"frankie"},
{x:157, y:55, name:"joeiyei"},
{x:157, y:62, name:"nickaie"},
{x:58, y:105, name:"tommie"}
]
d3.select('#mySvg')
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 6)
.attr('fill', 'blue')
d3.select('#mySvg')
.selectAll('text')
.data(myData)
.enter()
.append('text')
.attr('x', d => d.x + 6)
.attr('y', d => d.y + 3)
.attr('font-size', '0.8em')
.text(d => d.name)
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg id="mySvg">
我正在努力寻找从哪里开始使用 d3.force 文档。我应该将整个图转换为力图吗?我不介意某些点重叠,并且在我的数据中(以及在此示例中),某些点具有完全相同的 x,y 坐标。
另外,标签和点之间是否可以有一条浅灰色的线连接,这样就不会混淆哪个标签对应哪个点?
抱歉,如果以前有人问过这个问题。在我努力寻找这个问题的答案时,我发现了一些完全是力图的图(我认为我不想要)。任何帮助在这里表示赞赏!!!
解决方案
首先,不要将整个图表变成力导向图表。鉴于我在您的代码中看到的内容,点的精确垂直和水平位置对信息进行编码,该信息必须准确地传输给用户。根据其定义,力导向图表将无法保证如此精确。
您可以使用力模拟来避免标签的碰撞,也就是说,仅对文本应用模拟而不对点应用模拟。这并不难,但即便如此,我还是建议不要这样做。这里的原因很简单:你有文本标签,而不是像圆圈这样的图形元素。这些文本不能自由旋转,也不应该旋转(这会使阅读更加困难)。
因此,鉴于文本应该水平呈现,在这种情况下我最喜欢的方法是使用text-anchor
. 连同尽可能扩大散布以充分利用可用区域(这里我使用简单的线性比例),这种方法可能非常有效。这很简单:
首先,我们创建一个名称数组,显示在点的左侧:
const namesToLeft = ["dennie", "gregie"];
然后,我们更改text-anchor
:
.attr("text-anchor", function(d) {
return namesToLeft.indexOf(d.name) > -1 ? "end" : "start"
})
这是演示:
const myData = [{
x: 25,
y: 30,
name: "tommy"
},
{
x: 8,
y: 12,
name: "joey"
},
{
x: 92,
y: 107,
name: "nicky"
},
{
x: 85,
y: 50,
name: "peter"
},
{
x: 65,
y: 80,
name: "mickie"
},
{
x: 65,
y: 80,
name: "gregie"
},
{
x: 54,
y: 6,
name: "tammie"
},
{
x: 102,
y: 42,
name: "benny"
},
{
x: 66,
y: 45,
name: "dennie"
},
{
x: 81,
y: 44,
name: "jerryi"
},
{
x: 127,
y: 36,
name: "garrie"
},
{
x: 62,
y: 30,
name: "frankie"
},
{
x: 157,
y: 55,
name: "joeiyei"
},
{
x: 157,
y: 62,
name: "nickaie"
},
{
x: 58,
y: 105,
name: "tommie"
}
];
var scale = d3.scaleLinear()
.domain([0, 160])
.range([0, 300]);
const namesToLeft = ["dennie", "gregie"];
d3.select('#mySvg')
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => scale(d.x))
.attr('cy', d => scale(d.y))
.attr('r', 6)
.attr('fill', 'blue')
d3.select('#mySvg')
.selectAll('text')
.data(myData)
.enter()
.append('text')
.attr("text-anchor", function(d) {
return namesToLeft.indexOf(d.name) > -1 ? "end" : "start"
})
.attr('x', d => scale(d.x) + (namesToLeft.indexOf(d.name) > -1 ? -6 : 6))
.attr('y', d => scale(d.y) + 3)
.attr('font-size', '0.8em')
.text(d => d.name)
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg id="mySvg" width="400" height="300">
推荐阅读
- python - 在我的博客上 - 匹配的查询不存在
- ios - 如何创建自定义 MKMarkerAnnotationView 图像?
- php - 将 PHP 变量从我的索引页面传递到我的上传脚本时遇到问题
- wpf - 从其他项目中引用 .png
- python - 达到名称相同的字典的特定值
- javascript - 我在 html 和 js 中保存文件时遇到问题
- javascript - 如何在 React Native 中从图片库中读取 QR 码?
- javascript - 在 div "grand-parent" 顶部显示绝对 div
- node.js - 从 XML 文件中获取属性值
- spring - 如何在 Spring Boot 中动态创建多个数据源(在应用程序运行时)并为每个数据源关联事务管理器