d3 svg path添加文本_dagre-d3绘制流程图实用指南

写在前面

之前有小伙伴问我如何使用 D3 在前端绘制流程图,今天在这里给安排上,与大家分享。

明确一点,只要你的数据计算能力足够强,使用原生D3绘制流程图绝对可以的,但是,为了让大家更容易上手,避免重复造轮子,给大家推荐一个专门绘制流程图的 D3 插件 dagre-d3。

首先认识下 dagre。dagre 是专注于有向图布局的 javascript 库,由于 dagre 仅仅专注于图形布局,需要使用其他方案根据 dagre 的布局信息来实际渲染图形,而 dagre-d3 就是 dagre 基于 D3 的渲染方案。

dagre 项目地址:

dagrejs/dagre​github.com

dagre-d3 项目地址:

dagrejs/dagre-d3​github.com

下面使用 D3 与 dagre-d3 绘制新冠疫情期间的流动人员检测流程图,老规矩先上效果图。

dd7728e535bd3ca4f6aeb06c7b78a8c1.png

绘制基本流程图

数据准备

流程图作为一种有向图,与树图、网络图一样,数据由节点以及两点之间的边组成。

let 

绘图

使用 dagre-d3 绘制流程图分为以下个步骤

  1. 引入 d3.js 以及 dagre-d3.js 两个文件。
  2. 使用 dagre-d3 创建 Graph 对象,并添加节点和边。
  3. 创建渲染器并在 svg 上绘制流程图。
//引入 d3,dagre-d3

这样,前面的流程图就绘制出来了,如此简单,赶紧动手试一试!

如果你的需求远不止如此,要绘制更加高级的流程图,那么,还需要了解一下 dagre 里的基本概念以及相关配置项,深入掌握 dagre 与 d3-dagre 。


基本概念

dagre 是基于《A Technique for Drawing Directed Graphs》[1] 的理论实现图布局的。这篇文章的理论包含了以下5个重要概念:

  • graph,即图整体,用来配置图的全局参数。
  • node,即顶点,dagre 在计算时并不关心 node 实际的形状、样式,只要求提供维度信息。
  • edge,即边,edge 需要声明其两端的 node 以及本身方向。例如A -> B表示一条由 A 指向 B 的 edge。
  • rank,即层级,rank 是流程图布局中的核心逻辑单位,edge 两端的 node 一定属于不同的 rank,而同一 rank 中的 node 则会拥有同样的深度坐标(例如在纵向布局的 graph 中 y 坐标相同)。不理解没关系,先有个印象,后面会用示例进一步解释 rank 的作用。
  • label,即标签,label 不是必要元素,但 dagre 为了适用更多的场景增加了对 edge label 的布局计算。

理解 rank

接下来的示例中我们会用一种易懂的描述语言表达一个 node 与 edge:A -> B代表 A 和 B 两个 node 以及一条由 A 指向 B 的 edge。

示例 1

A->B; B->C;

+---+       +---+        +---+  
| A |------>| B |------->| C |  
+---+       +---+        +---+

这个例子里,node A, B, C 分别属于 3 个 rank。

示例 2

A->B; A->C;

            +---+
        --> | B |
+---+--/    +---+
| A |
+---+--    +---+
        --> | C |
            +---+

A 在 rank1 中,而 B 和 C 都比 A 低一个层级,属于 rank2,因此 B 和 C 拥有同样的 x 坐标(示例图为横行延伸,因此深度方向为 x 方向)。

示例 3

A->B; B->C; A->C;

            +---+
         -->| B |---
+---+---/   +---+    --->+---+  
| A |                    | C |  
+---+------------------->+---+

在这个示例中,我们发现 edge 两端的 node 可以相差超过一个 rank。由于 edge 两端的 node 不可属于同样的 rank,所以我们不能让 B 和 C 属于同一个 rank,进而最优的绘制结果为 A 和 C 之间相隔1个 rank。即 A 在 rank1 中,B 在 rank2 中,C 在 rank3 中。

配置项

理解了 drage 里的基本概念,下面我们看看 drage 提供了哪些配置项。

drage 的配置项主要可以分为 graph 图全局配置、node 、edge 三个部分。

graph 配置

  • rankdir

设置 node 节点的延伸排列方向,它有4个值: TB, BT, LR, 或者 RL 可选,默认是'TB'(从上到下)。这里T = top, B = bottom, L = left, and R = right

  • align

设置相同 rank 中 node 节点的对齐方式,它也有4个值可选,UL(上左), UR(上右), DL(下左), 或者 DR(下右),默认是 undefined 。这里U = up, D = down, L = left, and R = right。

  • nodesep 即 相同层级 rank 中 node 的间距。默认 50
  • edgesep 即 edge 之间的间距。默认 10
  • ranksep 即相邻层级之间的间距,例如 示例 1 中 A 和 B 的间距以及 B 和 C 的间距。默认 50
  • marginx 即 图整体与画布的左右间距。默认 0
  • marginy 即 图整体与画布的上下间距。默认 0

这些间距是不是有点晕,没关系,相信这张图能帮助你理解。

ea2f69af41ec695d7d92b468dcf0726c.png

注意:相邻两个节点不跨层级时,nodesep 和ranksep 实际是一样的。

为了便于理解这些属性,下面还是继续上图,毕竟一图胜千言。

g.setGraph({
   rankdir:'LR', //默认'TB'
   align:'DL',
   nodesep: 100,
   edgesep:100,
   ranksep: 50,
   marginx:50,
   marginy:100
});
  • rankdir 设置为 'LR' 意味着图布局从左向右延伸。

faae75b9b71bf74f60a679596f2f68d6.png
  • align 设置为 'DR' 意味着同层级右下对齐。

db816db5aa450187c8c6aec2a736b4e3.png
  • nodesep ranksep edgesep 设置后的样式。

fd560fddfdbf2ef353a2e7f1c2aeadf4.png

node 配置

  • labelType节点标签格式,可以设置文本以及html格式,默认为文本格式。
  • label 节点标签,即节点上要显示的文本,设置html格式时,label为html标签。
  • shape 节点形状,可以设置rect,circle,ellipse,diamond 四种形状,还可以使用render.shapes()自定义形状。
  • style 节点样式, 可设置节点的颜色填充、节点边框,如style: "fill:#fff;stroke:#faf"
  • labelStyle 节点标签样式, 可设置节点标签的文本样式(颜色、粗细、大小),如style: "fill:#afa;font-weight:bold"
  • width 即节点宽度。
  • height 即节点高度。

配置一下 nodes,看下我们的流程图会变成什么样子?

g.setNode(item.id, {
    //节点标签
    label: item.label,
//节点形状
    shape: item.shape,
    //节点样式
    style: "fill:#faf;stroke:#faf",
    //节点标签样式
    labelStyle:"fill:#afa"
})

f6af33018c15356589eab99c63f86e20.png

edge 配置

  • labelType节点标签格式,可以设置文本以及 html 格式,默认为文本格式。
  • label 节点标签,即节点上要显示的文本,设置 html 格式时,label为 html 标签。
  • style 边样式, 可设置边的颜色填充、边框,如style: "fill:#fff;stroke:#faf"
  • labelStyle 边标签样式, 可设置边标签的文本样式(颜色、粗细、大小),如labelStyle: "fill:#afa;font-weight:bold"
  • arrowhead 箭头形状,可以设置 normal,vee,undirected 三种样式,默认为 normal。
  • arrowheadStyle 箭头样式,可以设置箭头颜色等,如 arrowheadStyle:"fill:#f66"。

配置一下 edges,看下我们的流程图会变成什么样子?

g.setEdge(item.source,item.target, {
   //边标签
   label: item.label,
   //边样式
   style: "fill:#fff;stroke:#afa;stroke-width:2px",
   labelStyle: "fill:#1890ff",
   arrowhead:"vee",
   arrowheadStyle:"fill:#f66"
})

3f63e0ee186fdd8f25c9c5387b221563.png

学会了 dagre 的配置,就可以随心所欲的去绘制你想要的流程图了。但是,如果想要鼠标悬停、放大流程图等各种交互呢?这就需要 使用 D3 的 API 了。

交互

想要增加交互功能,就要用到 D3 的功能了。下面以拖拽缩放、鼠标悬浮显示提示 tooltip 为例。

拖拽缩放

let svg = d3.select('svg')
// 建立拖拽缩放
let zoom = d3.zoom()
    .on("zoom", function () {
        svgGroup.attr("transform", d3.event.transform);
     });
svg.call(zoom);

鼠标悬停 tootip

  • js代码
//创建提示框
function createTooltip() {
   return d3.select('body')
          .append('div')
          .classed('tooltip', true)
          .style('opacity', 0)
          .style('display', 'none');
    };
let tooltip = createTooltip();
//tooltip显示
function tipVisible(textContent) {
    tooltip.transition()
           .duration(400)
           .style('opacity', 0.9)
           .style('display', 'block');
     tooltip.html(textContent)
            .style('left', (d3.event.pageX + 15) + 'px')
            .style('top', (d3.event.pageY + 15) + 'px');
        }
//tooltip隐藏
function tipHidden() {
     tooltip.transition()
            .duration(400)
            .style('opacity', 0)
            .style('display', 'none');
 }

 //鼠标悬停显示隐藏tooltip
svgGroup.selectAll("g.node")
    .on("mouseover", function (v) {
       tipVisible(g.node(v).label);
     })
    .on("mouseout", function (v) {
        tipHidden();
     })
  • css 样式
.tooltip {
     position: absolute;
     font-size: 12px;
     text-align: center;
     background-color: white;
     border-radius: 3px;
     box-shadow: rgb(174, 174, 174) 0px 0px 10px;
     cursor: pointer;
     display: inline-block;
     padding:10px;
 }

.tooltip>div {
     padding: 10px;
 }

这篇文章关于 dagre 部分参考了Graph 数据可视化:JS 自动布局有向无环图[2]。

最后,贴上这篇文章的源码地址:

D3绘制流程图​xuxiaoyang.github.io

如果这篇文章对你有帮助,不要吝啬,点个

References

[1] 《A Technique for Drawing Directed Graphs》: http://www.graphviz.org/Documentation/TSE93.pdf

[2] Graph 数据可视化:JS 自动布局有向无环图: Graph 数据可视化:JS 自动布局有向无环图


关注 数据可视化技术 微信公众号

http://weixin.qq.com/r/GjkkPPnEmqTlrb1f92wI (二维码自动识别)