d3.js 重力节点布局_节点间多关系显示处理

本文介绍了如何在d3.js的重力布局中处理节点间多关系,确保多条线不叠加并保持美观。通过统计两点间的线条数并分配到map中,根据节点名设置方向和编号,利用tick方法配置线条样式,实现曲线连接。同时讨论了linePadding参数对箭头布局的影响,提供了解决箭头拥挤的方法。
摘要由CSDN通过智能技术生成

d3重力布局——节点间多关系处理

相关的知识:

path相关的知识

<path>标签是d3里面功能最为丰富的标签,很多图形用该标签可以制作出来。它通过一系列的坐标点来绘制。在d3的图形绘制中。经常会用到。

用法:给出一个坐标点,在坐标点前面添加一个英文字母,表示如何运动到此坐标点。

英文字母的按照功能可分为5类:

  • 移动类
    • M = moveto:将画笔移动到指定坐标
  • 直线类
    • L = lineto:画直线到指定坐标。
    • H = horizontal lineto:画水平线到指定坐标。
    • V = vertical lineto:画垂直线到指定坐标。
  • 曲线类
    • C = curveto:画三次贝塞尔曲线经两个指定控制点到达最终坐标。
    • S = shorthand/smooth curveto:与前一条三次贝塞尔曲线,第一个控制点为前一条曲线第二个控制点的对称点,只需要输入第二个控制点和终点,即可绘制一个三次贝塞尔曲线
    • Q = quadratic Bezier curveto:画一个二次贝塞尔曲线经过一个指定控制点到达终点坐标
    • T = Shorthand/smooth quadratic Bezier curveto:与前一条贝塞尔曲线相连,控制点为前一条二次贝塞尔曲线控制点的对称点。只需输入终点,即可绘制一个二次贝塞尔曲线。
  • 弧线类
    • A = elliptical arc:画椭圆曲线到指定坐标
  • 闭合类
    • Z = closepath:绘制一条直线,连接终点和起点,用来封闭图形。

到这里,我们知道了绘制一个<path>所要知道的基本的英文字母的基本意思

问题解析

然后就开始处理我们的核心问题:节点间的多关系

在实际应用场景中,人与人之间的关系不仅仅是单向,更多的是多项的,例如:小明 — 朋友 — 小红 ,小明 — 同学 — 小红 ;或者 小红— 朋友 — 小明 ,小红 — 同学 — 小明。

如果在d3的重力导向布局中如果不对这种节点间的关系做处理,很多关系会叠加载一块,影响美观与观感。
在这里插入图片描述
在现有的数据可视化框架中也有相关案例,例如echarts。你只需要设置lineStyle的属性curveness的值就可以使得线条呈现一个弧度。但是由于项目的特殊性,很多情况下需要我们用d3来完成自由度更高,并且一些个定制化的服务。
在这里插入图片描述
因此,在d3关于节点间的多关系处理中,曲线连接是一种比较好的方案。因此解决问题的核心在于如何绘制曲线,并且保证两点之间的多条线不会覆盖?在多条线弯曲下,如何平均半圆弧弯曲避免全跑到某半圆弧上?定义曲线弧方向?

首先统计下两点之间的线条数,再将这些连接线分配到一个 map 里,两个节点的 name 字段进行拼接做成 key,这样计算得到两点之间的连接线总数。

然后在遍历时同 map 的线根据方向分成正向、反向两组,正向组遍历给每条线追加设置一个 linknum 编号,同理,反向组遍历追加一个 -linknum 编号值。这个正向、反向判断方法很多,根据节点 source.name、target.name 进行比较,这里其实是比较 ASCII 码。而我们设定的 linknum 值就是来确定该条弧线的弯曲度和弯曲方向的,这里搭配下面代码讲解比较好理解:

关系处理

//在vue的methods的方法中
dealRealtions(links) {
       
      var linkGroup = {
   };  //用来分组,将两点之间的连线进行归类
      var linkMap = {
   };  //对连接线的计数
      for (var i = 0; i < links.length; i++) {
   
        var key = links[i].source.name < links[i].target.name ? links[i].source.name + ':' + links[i].target.name : links[i].target.name + ':' + links[i].source.name;
        if (!linkMap.hasOwnProperty(key)) {
   
          linkMap[key] = 0 ;
        }
        linkMap[key] += 1;
        if (!linkGroup.hasOwnProperty(key)) {
   
          linkGroup[key] = [];
        }
        linkGroup[key].push(links[i]
以下是一个简单的D3.js力导向图示例,演示如何添加新节点关系连线: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js力导向图 - 新增节点关系连线示例</title> <script src="https://d3js.org/d3.v6.min.js"></script> </head> <body> <svg width="800" height="600"></svg> <script> // 定义节点关系数据 var nodes = [ { id: "node1", name: "节点1" }, { id: "node2", name: "节点2" }, { id: "node3", name: "节点3" }, { id: "node4", name: "节点4" } ]; var links = [ { source: "node1", target: "node2" }, { source: "node1", target: "node3" }, { source: "node2", target: "node4" } ]; // 创建力导向图对象 var simulation = d3.forceSimulation(nodes) .force("link", d3.forceLink(links).id(d => d.id)) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(400, 300)); // 创建节点关系连线 var svg = d3.select("svg"); var link = svg.selectAll("line") .data(links) .enter().append("line") .attr("stroke", "#999") .attr("stroke-opacity", 0.6) .attr("stroke-width", d => Math.sqrt(d.value)); var node = svg.selectAll("circle") .data(nodes) .enter().append("circle") .attr("r", 10) .attr("fill", "#ccc") .call(drag(simulation)); var label = svg.selectAll("text") .data(nodes) .enter().append("text") .text(d => d.name) .attr("font-size", "12px") .attr("dx", 15) .attr("dy", 4); // 定义拖拽行为 function drag(simulation) { function dragstarted(event, d) { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragended(event, d) { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } return d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended); } // 新增节点关系连线 var newNode = { id: "node5", name: "节点5" }; var newLink = { source: "node4", target: "node5" }; nodes.push(newNode); links.push(newLink); node = node.data(nodes, d => d.id); node.exit().remove(); node = node.enter().append("circle") .attr("r", 10) .attr("fill", "#ccc") .call(drag(simulation)) .merge(node); label = label.data(nodes, d => d.id); label.exit().remove(); label = label.enter().append("text") .text(d => d.name) .attr("font-size", "12px") .attr("dx", 15) .attr("dy", 4) .merge(label); link = link.data(links); link.exit().remove(); link = link.enter().append("line") .attr("stroke", "#999") .attr("stroke-opacity", 0.6) .attr("stroke-width", d => Math.sqrt(d.value)) .merge(link); // 更新力导向图 simulation.nodes(nodes); simulation.force("link").links(links); simulation.alpha(1).restart(); </script> </body> </html> ``` 在上面的示例中,我们首先定义了一个简单的节点关系数据,并创建了一个力导向图对象。然后,我们使用D3.js创建了节点关系连线的SVG元素,并绑定了数据。 接下来,我们定义了一个拖拽行为,以便用户可以拖动节点。然后,我们添加了一个新节点和一个新的关系连线,并使用D3.js更新了节点关系连线的SVG元素。 最后,我们更新了力导向图对象,并重新启动了力导向图的模拟,以确保新节点关系连线被正确地添加到力导向图中。 请注意,这只是一个简单的示例,实际应用中需要根据具体需求进行更复杂的处理
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值