当前位置:   article > 正文

【D3.js - v5.x】(6)绘制树状图 | 层级布局 | 附完整代码_d3做一个不同省份和地区的树图

d3做一个不同省份和地区的树图

树状图

在d3 中,绘制树状图,要用到层级布局这个概念:

d3.hierarchy(data[, children])
  • 1

根据指定的层次结构数据构造一个根节点。指定的数据 data 必须为一个表示根节点的对象。比如:

{
  "name": "Eve",
  "children": [
    {
      "name": "Cain"
    },
    {
      "name": "Seth",
      "children": [
        {
          "name": "Enos"
        },
        {
          "name": "Noam"
        }
      ]
    },
    {
      "name": "Abel"
    },
    {
      "name": "Awan",
      "children": [
        {
          "name": "Enoch"
        }
      ]
    },
    {
      "name": "Azura"
    }
  ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

指定的 children 访问器会为每个数据进行调用,从根 data 开始,并且必须返回一个数组用以表示当前数据的子节点,返回 null 表示当前数据没有子节点。如果没有指定 children 则默认为:

function children(d) {
  return d.children;
}
  • 1
  • 2
  • 3

返回的节点和每一个后代会被附加如下属性:

在这里插入图片描述

文档: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;
    }
    
    
    • 1
    • 2
    • 3
    • 4

    一种更适合于径向布局的变体,可以按比例缩小半径差距:

    function separation(a, b) {
      return (a.parent == b.parent ? 1 : 2) / a.depth;
    }
    
    • 1
    • 2
    • 3

    间隔访问器用来设置相邻的两个节点之间的间隔。

绘制

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:"哈密"}
    			]
			}
		]
	};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

2. 创建层级布局

var hierarchyData = d3.hierarchy(dataset)
  .sum(function(d){
  return d.value;
});
  • 1
  • 2
  • 3
  • 4

3. 创建一个树状图

//创建一个树状图
var tree = d3.tree()
.size([width-400,height-200])
.separation(function(a,b){
return (a.parent==b.parent?1:2)/a.depth;
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4. 初始化树状图,也就是传入数据,并得到绘制树基本数据

var treeData = tree(hierarchyData);
  • 1
var nodes = treeData.descendants();
var links = treeData.links();
  • 1
  • 2

5. 创建一个贝塞尔生成曲线生成器

var Bézier_curve_generator = d3.linkHorizontal()
    		.x(function(d) { return d.y; })
    		.y(function(d) { return d.x; });
  • 1
  • 2
  • 3

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意,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+")";
    		});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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;
  })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

完整代码

<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/132670
推荐阅读
相关标签
  

闽ICP备14008679号