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