树形数据可视化
使用d3.js对层级数据进行可视化只需要考虑两步:
- 数据预处理
- data-join
width和height
-
Height:以本节点为根节点的树的高度
-
Width:与整棵树的根节点的路径长度
eg:
如11和15两个节点
- 11节点的Height:4,Width:0
- 15节点的Height:3,Width:1
数据处理
层级数据的数据预处理要分为两步
-
root1 = d3.hierarchy(data) //第一步预处理,转化成层级结构 // 返回的是层级结构的数据
-
root2 = d3.tree().size([innerHeight,innerWidth])(root1) //对层级结构进行空间上的划分/支配 d3.tree().size()//返回的是函数,可以直接调用,传入层级结构数据可得最终结果
下面给出root1 和 root2的数据结构
root1 层级结构
root2 树形结构(添加了空间信息,‘x ,y 坐标’)
d3.hierarchy() 做了啥?
- 保持数据的原始结构,并将输入层级数据转化成为d3中的hierarchy对象,同时引入下面的属性
- height
- depth
- children
- parent
- data (原始数据的映射)
- d3.hierarchy() 得到的数据结构可以作为一个 “ 中间结果 ”,继续输入到更多D3.js 提供的数据预处理的接口中
d3.tree() 做了啥?
- 可以将处理好的数据 ‘进一步’ 预处理,将 hierarchy() 处理的节点映射到空间中特定的位置,加上x,y 坐标属性
绘图
画Path
这里有一个小Tips:
- 如果想让树形图从上到下,那么设置d属性时,“x,y” 就与data中的 x,y 一致
- 如果想让树形图从左到右,那么设置d属性时, “x , y” 就与data中的x,y相反
g.selectAll('path').data(root.links()).join('path')
.attr('fill','none' )
.attr('stroke', 'black')
.attr('d', d3.linkHorizontal().x(d => d.y).y(d => d.x))
//看最后attr设置d时候,x与y 设置的值是相反的
画circle(结点)
circle的 rx,ry 要与path中的x ,y 对应
//画节点
g.selectAll('circle').data(root.descendants()).join('circle')
.attr('cx', d => d.y)
.attr('cy', d => d.x)
.attr('fill',fill)
.attr('stroke-width',3 )
.attr('r', 6)
//root.descendants() 按照广度优先的原则将 树形结构数据压平,返回一个list
画结点的文字信息
//文字
g.selectAll('text').data(root.descendants()).join('text')
.attr('font-size', '1em' )
.attr('text-anchor', d => d.children ? 'end' : 'start')
.attr('x', d => (d.children ? -6 : +6) + d.y)
.attr('y', d => d.x +5)
.attr('dy', d => d.children ? 2 : 0 )
.text( d => d.data.name)
//text-anchor 属性: d.children如果没有的话会为undifined,而undifined在逻辑表达式中为空。而如果有孩子,就让它从右往左扩展,不让孩子的路径遮盖text,如果没有孩子,就让它从左往右扩展
// x 与 y 的属性设置: 有孩子的节点和没有孩子的节点设置是不同的,为了避免path遮挡住文字
可视化代码
<!DOCTYPE html>
<html>
<head>
<title>Tree</title>
<script src="./js/d3.min.js"></script>
</head>
<body>
<svg width="1600" height="940" id="mainsvg" class="svgs"
style="display: block; margin: auto;"></svg>
<script>
const svg = d3.select('#mainsvg');
const width = +svg.attr('width');
const height = +svg.attr('height');
const margin = {top: 50, right: 150, bottom: 50, left: 60};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
let root;
let color;
const fill = d => {
if( d.depth == 0 ){
//根节点
return color(d.data.name);
}
while (d.depth >1 ){
//不是第二层
d = d.parent;
}
return color(d.data.name)
}
const render = function(){
color = d3.scaleOrdinal(d3.schemeCategory10)
// 比较简单的办法 ,动态分配配色
//下面是比较复杂的方法
// .domain(root.descendants().filter(d => d.depth<=1).map(d => d.data.name))
// .range(d3.schemeCategory10);
//画路径
g.selectAll('path').data(root.links()).join('path')
.attr('fill','none' )
.attr('stroke', 'black')
.attr('d', d3.linkHorizontal().x(d => d.y).y(d => d.x))
//如果把x和y 的值反过来就会变成一颗从上到下的树
//画节点
g.selectAll('circle').data(root.descendants()).join('circle')
.attr('cx', d => d.y)
.attr('cy', d => d.x)
.attr('fill',fill)
.attr('stroke-width',3 )
.attr('r', 6)
//root.descendants() 按照广度优先的原则将 树形结构数据压平,返回一个list
//文字
g.selectAll('text').data(root.descendants()).join('text')
.attr('font-size', '1em' )
.attr('text-anchor', d => d.children ? 'end' : 'start')
.attr('x', d => (d.children ? -6 : +6) + d.y)
.attr('y', d => d.x +5)
.attr('dy', d => d.children ? 2 : 0 )
.text( d => d.data.name)
//text-anchor 属性: d.children如果没有的话会为undifined,而undifined在逻辑表达式中为空。而如果有孩子,就让它从右往左扩展,不让孩子的路径遮盖text,如果没有孩子,就让它从左往右扩展
// x 与 y 的属性设置: 有孩子的节点和没有孩子的节点设置是不同的,为了避免path遮挡住文字
}
d3.json('./data/games.json').then(data => {
root = d3.hierarchy(data);//第一步预处理,转化成层级结构
root = d3.tree().size([innerHeight,innerWidth])(root) ; //对层级结构进行空间上的划分/支配
render(); //data join
})
</script>
</body>
</html>