上一篇: 桑基图 https://blog.csdn.net/zjw_python/article/details/98611559

下一篇: 力导向图 https://blog.csdn.net/zjw_python/article/details/98617650

代码结构和初始化画布的Chart对象介绍,请先看 https://blog.csdn.net/zjw_python/article/details/98182540

本图完整的源码地址: https://github.com/zjw666/D3_demo/tree/master/src/sunburst/basicSunburst

1 图表效果

2 数据

{"name": "alphabet","children": [{"name": "a","children": [{"name": "a1","children": [{"name": "a11", "house": 2},{"name": "a12","children":[{"name" : "a121","house" : 2},{"name" : "a122","house" : 1}]},{"name": "a13", "house": 4}]}]},{"name": "b","children": [{"name": "b1","children": [{"name": "b12","children":[{"name" : "b121","house" : 1},{"name" : "b122","house" : 1}]},{"name": "b12", "house": 2}]},{"name": "b2","children": [{"name": "b21","children":[{"name" : "b211","house" : 3}]},{"name" : "b22","house" : 2}]}]},{"name": "c","children": [{"name": "c1","children": [{"name": "c22","children":[{"name" : "c221","children": [{"name" : "c2211", "house" : 3}]},{"name" : "c222", "house" : 1}]},{"name": "c22","children":[{"name" : "c221","children": [{"name" : "c2211", "house" : 1}]}]}]},{"name": "c2","children": [{"name": "c21", "house": 2},{"name": "c22","children":[{"name" : "c221","children": [{"name" : "c2211", "house" : 4}]}]},{"name": "c23", "house": 2}]},{"name": "c3","children": [{"name": "c22","children":[{"name" : "c221","children": [{"name" : "c2211", "house" : 1},{"name" : "c2212", "house" : 2}]}]},{"name": "c22","children":[{"name" : "c221","house" : 1}]}]}]}]
}

3 关键代码

导入数据

d3.json('./data.json').then(function(data){.....

一些样式参数配置

const config = {margins: {top: 80, left: 50, bottom: 50, right: 50},textColor: 'black',title: '基础旭日图',hoverColor: 'gray',animateDuration: 1000,}

数据转换,为了之后动画过渡效果,这里给每个数据项都表示唯一性ID

chart._nodeId = 0;  //用于标识数据唯一性data = addId(data);function addId(d){     //给数据标识唯一性Idd.id = ++ chart._nodeId;if (d.children){d.children.forEach((item) => addId(item))}return d;
}

旭日图也是层次型数据结构,使用d3.hierarchy生成节点后,运用d3.partition为节点添加布局信息

const root = d3.hierarchy(data).sum((d) => d.house).sort((a,b) => a.value - b.value);chart.currentRoot = root.data.id;     //记录当前旭日图中心根节点idconst partition = d3.partition().size([chart.getBodyWidth(), chart.getBodyHeight()]).round(true);partition(root);let nodes = root.descendants();

尺度转换,由于布局函数添加的布局信息是笛卡尔坐标系的,因此我们要将值转换成极坐标系下的度量值

/* ----------------------------尺度转换------------------------  */chart.scaleXToAngle = d3.scaleLinear().domain([0, chart.getBodyWidth()]).range([0, Math.PI * 2]);chart.scaleYToRadius = d3.scaleLinear().domain([0, chart.getBodyHeight()]).range([0, d3.min([chart.getBodyWidth(), chart.getBodyHeight()]) / 2]);

渲染扇形,这里为了过渡动画效果,达到Echart这种效果,对扇形的半径和角度这两个属性都应用了中间帧函数计算

/* ----------------------------渲染扇形------------------------  */chart.renderSlice = function(){const slices = chart.body().selectAll('.slice').data(nodes, (d) => d.data.id);chart.slicesEnter = slices.enter().append('g').attr('transform', 'translate(' + chart.getBodyWidth()/2 + ',' + chart.getBodyHeight()/2 + ')').attr('class', 'slice');chart.slicesEnter.append('path').attr('stroke', 'white').attr('fill', (d) => chart._colors(d.data.id % 10));chart.slicesEnter.merge(slices).select('path').transition().duration(config.animateDuration).attrTween('d', arcTween);slices.exit().remove();function arcTween(d){                //圆弧的角度和半径过渡动画let currentRadius = this._currentR;if (!currentRadius){currentRadius = chart.scaleYToRadius(d.y1)}const interpolateR = d3.interpolate(    //只对外半径插值currentRadius,chart.scaleYToRadius(d.y1),)let currentArc = this._current;if (!currentArc){currentArc = {startAngle: 0, endAngle: 0};}const interpolateArc = d3.interpolate(     //对弧度插值currentArc,{startAngle: chart.scaleXToAngle(d.x0),endAngle: chart.scaleXToAngle(d.x1)})this._current = interpolateArc(1);this._currentR = interpolateR(1);return function(t){let arc = d3.arc().outerRadius(interpolateR(t)).innerRadius(chart.scaleYToRadius(d.y0));return arc(interpolateArc(t))};}}

渲染文本标签,文本标签的位置通过三角三角函数计算得出,并根据其所在角度旋转自身,适应布局

/* ----------------------------渲染文本标签------------------------  */chart.renderText = function(){const slices = chart.body().selectAll('.slice');chart.slicesEnter.append('text').attr('class', 'text').attr('stroke', config.textColor).attr('dy', 5).attr('text-anchor', 'middle');chart.slicesEnter.merge(slices).select('text').attr('x', (d) => getTextLocation(d, 'x')).attr('y', (d) => getTextLocation(d, 'y')).attr('transform', (d) => {let rotateAngle = (chart.scaleXToAngle(d.x0) + chart.scaleXToAngle(d.x1)) / 2 * 180 / Math.PI;if (chart.scaleYToRadius(d.y0) === 0) rotateAngle = 0;return 'rotate(' + rotateAngle + ' '+ getTextLocation(d, 'x') + ','+ getTextLocation(d, 'y') +')';}).text('').transition().delay(config.animateDuration).text((d) => d.data.id);function getTextLocation(d, type){     //获取文本的x和y坐标let middleRadius = 0;let middleAngle = 0;if (chart.scaleYToRadius(d.y0) > 0){middleAngle = (chart.scaleXToAngle(d.x0) + chart.scaleXToAngle(d.x1))/2;middleRadius = (chart.scaleYToRadius(d.y1) + chart.scaleYToRadius(d.y0))/2;}if (type === 'x'){return Math.sin(middleAngle) * middleRadius;}else if (type === 'y'){return -Math.cos(middleAngle) * middleRadius;}}}

绑定鼠标交互事件,这里主要有两个逻辑,点击中心节点回退一层,以及点击其他节点下钻一层,注意在重新计算布局时,不能直接将原节点传入,因为数据下钻后,节点的实际depth属性发生改变,否则会导致新布局中扇形的半径过渡失败

/* ----------------------------绑定鼠标交互事件------------------------  */chart.addMouseOn = function(){d3.selectAll('.slice').on('click', function(d){if (d.data.id === chart.currentRoot){    //点击中心节点回退if (d.parent){const newD = d.parent.copy();newD.parent = d.parent.parent;partition(newD);nodes = newD.descendants();chart.currentRoot = d.parent.data.id;chart.renderSlice();chart.renderText();chart.addMouseOn();}}else{                        //点击其余节点下钻const newD = d.copy();newD.parent = d.parent;partition(newD);nodes = newD.descendants();chart.currentRoot = d.data.id;chart.renderSlice();chart.renderText();chart.addMouseOn();}});}

大功告成!!!


如果觉得这篇文章帮助了您,请打赏一个小红包鼓励作者继续创作哦!!!

D3 二维图表的绘制系列(二十三)旭日图相关推荐

  1. D3 二维图表的绘制系列(一)介绍

    1 介绍 D3 (或者叫 D3.js )是一个基于 web 标准的 JavaScript 可视化库. D3 可以借助 SVG, Canvas 以及HTML 将你的数据生动的展现出来. D3 结合了强大 ...

  2. D3 二维图表的绘制系列(二十二)桑基图sankey

    上一篇: 仪表盘图 https://blog.csdn.net/zjw_python/article/details/98596174 下一篇: 旭日图 https://blog.csdn.net/z ...

  3. D3 二维图表的绘制系列(六)基础折线图

    上一篇: 横向柱状图 https://blog.csdn.net/zjw_python/article/details/98209333 下一篇: 堆叠面积图 https://blog.csdn.ne ...

  4. D3 二维图表的绘制系列(二十六)盒须图

    上一篇: 中国地图 下一篇: 日历热力图 代码结构和初始化画布的Chart对象介绍,请先看 这里 本图完整的源码地址:这里 1 图表效果 2 数据 {"data": [{" ...

  5. D3 二维图表的绘制系列(十五)雷达图

    上一篇: 气泡图 https://blog.csdn.net/zjw_python/article/details/98485368 下一篇: 矩形树状图 https://blog.csdn.net/ ...

  6. D3 二维图表的绘制系列(二十七)日历热力图

    上一篇: 盒须图 下一篇: 弦图 代码结构和初始化画布的Chart对象介绍,请先看 这里 本图完整的源码地址: 这里 1 图表效果 2 数据 {"2018-01-01": 2,&q ...

  7. D3 二维图表的绘制系列(八)曲线图

    上一篇: 堆叠面积图 https://blog.csdn.net/zjw_python/article/details/98214359 下一篇: 基础饼图 https://blog.csdn.net ...

  8. D3 二维图表的绘制系列(二十)河流图

    上一篇: 封闭图 https://blog.csdn.net/zjw_python/article/details/98591118 下一篇: 仪表盘图 https://blog.csdn.net/z ...

  9. D3 二维图表的绘制系列(十四)气泡图

    上一篇: 多符号散点图 https://blog.csdn.net/zjw_python/article/details/98483989 下一篇: 雷达图 https://blog.csdn.net ...

最新文章

  1. zuul如果两个filter的order一样,是如何排序的?
  2. 工业物联网将借助新兴技术实现华丽转身
  3. vue - 减少打包后的体积
  4. Java基础-Eclipse第三方安装包管理工具之Maven
  5. python2中文导致的错误
  6. [翻译] python Tutorial 之一
  7. ajax跨域请求wcf,ajax wcf 指定某个域名 进行跨域访问
  8. android 编译模块
  9. Cesium教程系列汇总
  10. ENVI实验教程(6)实验六、遥感图像分类
  11. 用R命令看一下各个寄存器的设置情况
  12. 安装redis 5.0.5版本 真香警告
  13. 完成基于ICX285和ICX205两种CCD的兼容性电路设计
  14. 常见物联网操作系统介绍
  15. builder设计模式,写和很好
  16. CSS3鼠标悬停360度旋转效果
  17. 2018NOIp爆零记
  18. 电商领域知识图谱:常识抽取,表示与应用
  19. docker安装mysql 并挂载目录
  20. 郁 繁体为“鬰” 古同 “鬱”

热门文章

  1. python 图片自动分类机_用tensorflow神经网络实现一个简易的图片分类器
  2. 时间t与时间管理——柳比歇夫、德鲁…
  3. 【Mysql系列】MySQL创建数据库、CURD的操作
  4. linux驱动:二、LED灯驱动编写
  5. 第一个Java程序Hello World(IG牛逼)
  6. 绝地求生显示器测试软件,《绝地求生大逃杀》1728*1080分辨率怎么设置?自定义分辨率设置方法分享...
  7. Double与BigDecimal 比较
  8. 这套摄像头方案太适合监控院子了!(附:安装方法+物联卡推荐)
  9. Java集合框架详解
  10. 51单片机点亮LED灯以及实现2盏LED灯的交替闪烁