樹狀圖
在d3 中,繪製樹狀圖,要用到層級佈局這個概念:
d3.hierarchy(data[, children])
根據指定的層次結構數據構造一個根節點。指定的數據 data 必須爲一個表示根節點的對象。比如:
{
"name": "Eve",
"children": [
{
"name": "Cain"
},
{
"name": "Seth",
"children": [
{
"name": "Enos"
},
{
"name": "Noam"
}
]
},
{
"name": "Abel"
},
{
"name": "Awan",
"children": [
{
"name": "Enoch"
}
]
},
{
"name": "Azura"
}
]
}
指定的 children 訪問器會爲每個數據進行調用,從根 data 開始,並且必須返回一個數組用以表示當前數據的子節點,返回 null
表示當前數據沒有子節點。如果沒有指定 children 則默認爲:
function children(d) {
return d.children;
}
返回的節點和每一個後代會被附加如下屬性:
文檔:https://www.d3js.org.cn/document/d3-hierarchy/#installing
其中,
- node.descendants():返回後代節點數組,第一個節點爲自身,然後依次爲所有子節點的拓撲排序
- node.links():返回當前 node 的
links
數組, 其中每個 link 是一個定義了 source 和 target 屬性的對象。每個link
的source
爲父節點,target
爲子節點。
同時,需要和tree生成器一起使用,來得到繪製樹所需要的節點數據和邊數據。
文檔: https://www.d3js.org.cn/document/d3-hierarchy/#tree
其中,
-
d3.tree(),創建一個樹狀圖生成器,使用默認的設置創建一個新的樹佈局
-
d3.tree().size([size]),定義樹的大小。如果指定了 size 則設置當前系統樹佈局的尺寸爲一個指定的二元數值類型數組,表示 [width, height] 並返回當前樹佈局。如果 size 沒有指定則返回當前系統樹佈局的尺寸,默認爲 [1, 1]。如果返回的佈局尺寸爲
null
時則表示實際的尺寸根據 node size 確定。 -
d3.tree.nodeSize([size]),如果指定了 size 則設置系統樹佈局的節點尺寸爲指定的數值二元數組,表示爲 [width, height] 並返回當前樹佈局。如果沒有指定 size 則返回當前節點尺寸,默認爲
null
。如果返回的尺寸爲null
則表示使用 layout size 來自動計算節點大小。當指定了節點尺寸時,根節點的位置總是位於 ⟨0, 0⟩。 -
d3.tree().separation([separation]),定義鄰居節點的距離。如果指定了 seperation, 則設置間隔訪問器爲指定的函數並返回當前樹佈局。如果沒有指定 seperation 則返回當前的間隔訪問器,默認爲:
function separation(a, b) { return a.parent == b.parent ? 1 : 2; }
一種更適合於徑向佈局的變體,可以按比例縮小半徑差距:
function separation(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }
間隔訪問器用來設置相鄰的兩個節點之間的間隔。
繪製
1. 數據準備
//定義邊界
var marge = {top:50, bottom:0, left:10, right:0};
var svg = d3.select("svg");
var width = svg.attr("width");
var height = svg.attr("height");
var g = svg.append("g")
.attr("transform","translate("+marge.top+","+marge.left+")");
var scale = svg.append("g")
.attr("transform","translate("+marge.top+","+marge.left+")");
//數據
var dataset = {
name:"中國",
children:[
{
name:"浙江",
children:[
{name:"杭州" ,value:100},
{name:"寧波",value:100},
{name:"溫州",value:100},
{name:"紹興",value:100}
]
},
{
name:"廣西",
children:[
{
name:"桂林",
children:[
{name:"秀峯區",value:100},
{name:"疊彩區",value:100},
{name:"象山區",value:100},
{name:"七星區",value:100}
]
},
{name:"南寧",value:100},
{name:"柳州",value:100},
{name:"防城港",value:100}
]
},
{
name:"黑龍江",
children:[
{name:"哈爾濱",value:100},
{name:"齊齊哈爾",value:100},
{name:"牡丹江",value:100},
{name:"大慶",value:100}
]
},
{
name:"新疆" ,
children:
[
{name:"烏魯木齊"},
{name:"克拉瑪依"},
{name:"吐魯番"},
{name:"哈密"}
]
}
]
};
2. 創建層級佈局
var hierarchyData = d3.hierarchy(dataset)
.sum(function(d){
return d.value;
});
3. 創建一個樹狀圖
//創建一個樹狀圖
var tree = d3.tree()
.size([width-400,height-200])
.separation(function(a,b){
return (a.parent==b.parent?1:2)/a.depth;
})
4. 初始化樹狀圖,也就是傳入數據,並得到繪製樹基本數據
var treeData = tree(hierarchyData);
var nodes = treeData.descendants();
var links = treeData.links();
5. 創建一個貝塞爾生成曲線生成器
var Bézier_curve_generator = d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; });
6. 繪製邊
//繪製邊
g.append("g")
.selectAll("path")
.data(links)
.enter()
.append("path")
.attr("d",function(d){
var start = {x:d.source.x,y:d.source.y};
var end = {x:d.target.x,y:d.target.y};
return Bézier_curve_generator({source:start,target:end});
})
.attr("fill","none")
.attr("stroke","yellow")
.attr("stroke-width",1);
注意,attr(“d”,function(d)這個函數第二個參數的格式要求。
7. 常規:建立用來放在每個節點和對應文字的分組
var gs = g.append("g")
.selectAll("g")
.data(nodes)
.enter()
.append("g")
.attr("transform",function(d){
var cx = d.x;
var cy= d.y;
return "translate("+cy+","+cx+")";
});
8. 繪製節點和文字
//繪製節點
gs.append("circle")
.attr("r",6)
.attr("fill","white")
.attr("stroke","blue")
.attr("stroke-width",1);
//文字
gs.append("text")
.attr("x",function(d){
return d.children?-40:8;//如果某節點有子節點,則對應的文字前移
})
.attr("y",-5)
.attr("dy",10)
.text(function(d){
return d.data.name;
})
完整代碼
<body>
<svg width="1000" height="1000"></svg>
<script>
var marge = {top:60,bottom:60,left:60,right:60}
var svg = d3.select("svg")
var width = svg.attr("width")
var height = svg.attr("height")
var g = svg.append("g").attr("transform","translate("+marge.top+","+marge.left+")");
var scale = svg.append("g")
//1. 準備數據
var dataset = {
name:"中國",
children:[
{
name:"浙江",
children:[
{name:"杭州" ,value:100},
{name:"寧波",value:100},
{name:"溫州",value:100},
{name:"紹興",value:100}
]
},
{
name:"廣西",
children:[
{
name:"桂林",
children:[
{name:"秀峯區",value:100},
{name:"疊彩區",value:100},
{name:"象山區",value:100},
{name:"七星區",value:100}
]
},
{name:"南寧",value:100},
{name:"柳州",value:100},
{name:"防城港",value:100}
]
},
{
name:"黑龍江",
children:[
{name:"哈爾濱",value:100},
{name:"齊齊哈爾",value:100},
{name:"牡丹江",value:100},
{name:"大慶",value:100}
]
},
{
name:"新疆" ,
children:
[
{name:"烏魯木齊"},
{name:"克拉瑪依"},
{name:"吐魯番"},
{name:"哈密"}
]
}
]
};
//2. 創建層級佈局
var hierarchyData = d3.hierarchy(dataset)
.sum(function(d){
return d.value;
});
//3. 創建一個樹狀圖
var tree = d3.tree()
.size([width-400,height-200])
.separation(function(a,b){
return (a.parent==b.parent?1:2)/a.depth;
})
//4. 初始化樹狀圖,也就是傳入數據,並得到繪製樹基本數據
var treeData = tree(hierarchyData);
var nodes = treeData.descendants();
var links = treeData.links();
//5. 創建一個貝塞爾生成曲線生成器
var Bézier_curve_generator = d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; });
//6. 繪製邊
g.append("g")
.selectAll("path")
.data(links)
.enter()
.append("path")
.attr("d",function(d){
var start = {x:d.source.x,y:d.source.y};
var end = {x:d.target.x,y:d.target.y};
return Bézier_curve_generator({source:start,target:end});
})
.attr("fill","none")
.attr("stroke","yellow")
.attr("stroke-width",1);
//7. 常規:建立用來放在每個節點和對應文字的分組<g>
var gs = g.append("g")
.selectAll("g")
.data(nodes)
.enter()
.append("g")
.attr("transform",function(d){
var cx = d.x;
var cy= d.y;
return "translate("+cy+","+cx+")";
});
//8. 繪製節點和文字
gs.append("circle")
.attr("r",6)
.attr("fill","white")
.attr("stroke","blue")
.attr("stroke-width",1);
//文字
gs.append("text")
.attr("x",function(d){
return d.children?-40:8;//如果某節點有子節點,則對應的文字前移
})
.attr("y",-5)
.attr("dy",10)
.text(function(d){
return d.data.name;
})
</script>
</body>