上一篇: 漏斗图 https://blog.csdn.net/zjw_python/article/details/98497967
下一篇: 河流图 https://blog.csdn.net/zjw_python/article/details/98592543
代码结构和初始化画布的Chart对象介绍,请先看 https://blog.csdn.net/zjw_python/article/details/98182540
本图完整的源码地址: https://github.com/zjw666/D3_demo/tree/master/src/enclosureChart/basicEnclosure
1 图表效果
2 数据
{
"name": "grandfather",
"children": [
{
"name": "father",
"children": [
{
"name": "son",
"children": [
{"name": "grandson1", "house": 2},
{"name": "grandson2", "house": 3},
{"name": "grandson3", "house": 4}
]
}
]
},
{
"name": "mother1",
"children": [
{
"name": "daughter1",
"children": [
{"name": "granddaughter1", "house": 4},
{"name": "granddaughter2", "house": 2}
]
},
{
"name": "daughter2",
"children": [
{"name": "granddaughter3", "house": 4}
]
}
]
},
{
"name": "mother2",
"children": [
{
"name": "son1",
"children": [
{"name": "grandson4", "house": 6},
{"name": "granddaughter4", "house": 1}
]
},
{
"name": "son2",
"children": [
{"name": "granddaughter5", "house": 2},
{"name": "grandson5", "house": 3},
{"name": "granddaughter5", "house": 2}
]
}
]
}
]
}
3 关键代码
导入数据
d3.json('./data.json').then(function(data){
....
一些样式参数配置
const config = {
margins: {top: 80, left: 80, bottom: 50, right: 80},
textColor: 'black',
title: '基本封闭图',
hoverColor: 'white',
animateDuration: 1000
}
数据转换,封闭图与矩形树图、树图等都是具有层次的数据结构,因此需要将原始数据转换一下,转换后,运用d3.pack
添加布局信息
/* ----------------------------数据转换------------------------ */
const root = d3.hierarchy(data)
.sum((d) => d.house)
.sort((a,b) => a.value - b.value);
const pack = d3.pack()
.size([chart.getBodyWidth(), chart.getBodyHeight()])
pack(root);
有了布局信息之后,我们直接就用circle
元素渲染一个个圆圈,非常简单
/* ----------------------------渲染圆圈------------------------ */
chart.renderCircle = function(){
const groups = chart.body().selectAll('.g')
.data(root.descendants());
groups.enter()
.append('g')
.attr('class', (d, i) => 'g g-' + i)
.append('circle')
.attr('class', 'circle')
.merge(groups.selectAll('.circle'))
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y)
.attr('r', (d) => d.r)
.attr('fill', (d) => chart._colors(d.depth % 10));
groups.exit()
.selectAll('.circle')
.transition().duration(config.animateDuration)
.attr('r', 0)
.remove();
}
渲染文本,注意文本长度不要超过圆圈的直径
/* ----------------------------渲染文本标签------------------------ */
chart.renderText = function(){
const texts = chart.body().selectAll('.text')
.data(root.descendants());
texts.enter()
.append('text')
.attr('class', 'text')
.merge(texts)
.attr('transform', (d) => 'translate(' + d.x + ',' + d.y + ')' )
.text((d) => d.data.name)
.attr('stroke', config.textColor)
.attr('fill', config.textColor)
.attr('text-anchor', 'middle')
.text( function(d){
if (d.children) return;
if (textWidthIsOk(d, this)){
return d.data.name;
}else{
return d.data.name.slice(0,3);
}
})
// 检测文本长度是否合适
function textWidthIsOk(d, text){
const textWidth = text.getBBox().width;
if (d.r*2 >= textWidth) return true;
return false;
}
}
最后绑定鼠标交互事件,悬停鼠标圆圈变色
/* ----------------------------绑定鼠标交互事件------------------------ */
chart.addMouseOn = function(){
d3.selectAll('.g circle')
.on('mouseover', function(){
const e = d3.event;
e.target.style.cursor = 'hand'
d3.select(e.target)
.attr('fill', config.hoverColor);
})
.on('mouseleave', function(d){
const e = d3.event;
d3.select(e.target)
.attr('fill', chart._colors(d.depth % 10));
});
}