【D3.js - v5.x】(6)繪製樹狀圖 | 層級佈局 | 附完整代碼

樹狀圖

在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():返回當前 nodelinks 數組, 其中每個 link 是一個定義了 sourcetarget 屬性的對象。每個 linksource 爲父節點, 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>

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章