Constructing a node-link tree visualization
首先将节点之间的连线画出来。
使用json
函数读取文件以后,使用hierarchy
等函数得到连线的数组,然后绑定这个数组,给每个元素添加一个path
,绘画使用的是一个函数linkHorizontal
(因为这里是水平的树状图,如果你想绘制垂直的也可以使用linkVertical
,需要注意的是,水平的需要交换每个连线的x和y,垂直的不需要)
index.js
json('data.json').then(data =>{const root = hierarchy(data);const links = treeLayout(root).links();const linkPathGenerator = linkHorizontal().x(d => d.y).y(d => d.x)//上面的x和y进行了替换,是因为我们想要绘制水平的树状图,如果使用垂直的,x和y应该是对应的g.selectAll('path').data(links).enter().append('path').attr('d', linkPathGenerator);
}
style.css
path {fill: none;stroke: #f7a4a4;
}
Adding text labels to the nodes
通过root.descendants
获得每个节点的位置数组,绑定这个数组以后添加text
,同样需要注意水平的x
和y
需要分开。然后使用一些技巧使得文字变得更加好看
const treeLayout = tree().size([Height, Width]);json('data.json').then(data =>{const root = hierarchy(data);const links = treeLayout(root).links();const linkPathGenerator = linkHorizontal().x(d => d.y).y(d => d.x)//上面的x和y进行了替换,是因为我们想要绘制水平的树状图,如果使用垂直的,x和y应该是对应的g.selectAll('path').data(links).enter().append('path').attr('d', linkPathGenerator);g.selectAll('text').data(root.descendants()).enter().append('text').attr('x', d => d.y).attr('y', d => d.x).text(d => d.data.data.id);});
Using the Margin Convention(约定)
为了让文字布局更加好看,我们需要设置Margin
来设置边框
const margin = {top:0, right: 70, bottom: 0, left:90};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;const treeLayout = tree().size([innerHeight, innerWidth]);const zoomG = svg.attr('width', width).attr('height', height).append('g');const g = zoomG.append('g')
.attr('transform',`translate(${margin.left},${margin.top})`);
然后后面都在g
上添加元素即可。这里设置了两层g
实际上是为了后面放大缩小的时候使用
Tweaking(调整) label alignment(队列) and size
我们还需要设置标签的位置,标签的字体大小:
index.js
g.selectAll('text').data(root.descendants()).enter().append('text').attr('x', d => d.y).attr('y', d => d.x).attr('dy', '0.32em')//使得节点被线从中间穿过.attr('text-anchor', d => d.children ? 'middle' : 'start')//将文字放在中间.attr('font-size', d => 3.2-d.depth + 'em') //使得文字大小随层数递减.text(d => d.data.data.id);
style.css
text {text-shadow:
/* 给标签添加白色的阴影,这样就不会被线挡住 */-1px -1px 3px white,-1px 1px 3px white,1px -1px 3px white,1px 1px 3px white;pointer-events: none;
/* 鼠标经过文字的时候不会变成可编辑的样子(因为本来就是不可编辑的) */
}
Panning & Zooming
这个和以前一样,在call
函数里面添加zoom
函数:
index.js
svg.call(zoom().on('zoom',() =>{zoomG.attr('transform', event.transform);
}));
Curran说弄两层g
这样就可以解决放大再缩小以后边框丢失的问题,但是我发现好像并没有什么卵用。。。
Using a custom font
选择一个好看的字体也很重要,首先在Google Fonts里面找到一个喜欢的字体,然后点击select
然后把link
放在html
文件里面,后面的字体放在对应的选择器里面就可以了。
效果图:
代码地址:https://vizhub.com/Edward-Elric233/706152caf5ca4aae992cc371f2d5891a