D3 二维图表的绘制系列(二十三)旭日图
上一篇: 桑基图 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 二维图表的绘制系列(二十三)旭日图相关推荐
- D3 二维图表的绘制系列(一)介绍
1 介绍 D3 (或者叫 D3.js )是一个基于 web 标准的 JavaScript 可视化库. D3 可以借助 SVG, Canvas 以及HTML 将你的数据生动的展现出来. D3 结合了强大 ...
- D3 二维图表的绘制系列(二十二)桑基图sankey
上一篇: 仪表盘图 https://blog.csdn.net/zjw_python/article/details/98596174 下一篇: 旭日图 https://blog.csdn.net/z ...
- D3 二维图表的绘制系列(六)基础折线图
上一篇: 横向柱状图 https://blog.csdn.net/zjw_python/article/details/98209333 下一篇: 堆叠面积图 https://blog.csdn.ne ...
- D3 二维图表的绘制系列(二十六)盒须图
上一篇: 中国地图 下一篇: 日历热力图 代码结构和初始化画布的Chart对象介绍,请先看 这里 本图完整的源码地址:这里 1 图表效果 2 数据 {"data": [{" ...
- D3 二维图表的绘制系列(十五)雷达图
上一篇: 气泡图 https://blog.csdn.net/zjw_python/article/details/98485368 下一篇: 矩形树状图 https://blog.csdn.net/ ...
- D3 二维图表的绘制系列(二十七)日历热力图
上一篇: 盒须图 下一篇: 弦图 代码结构和初始化画布的Chart对象介绍,请先看 这里 本图完整的源码地址: 这里 1 图表效果 2 数据 {"2018-01-01": 2,&q ...
- D3 二维图表的绘制系列(八)曲线图
上一篇: 堆叠面积图 https://blog.csdn.net/zjw_python/article/details/98214359 下一篇: 基础饼图 https://blog.csdn.net ...
- D3 二维图表的绘制系列(二十)河流图
上一篇: 封闭图 https://blog.csdn.net/zjw_python/article/details/98591118 下一篇: 仪表盘图 https://blog.csdn.net/z ...
- D3 二维图表的绘制系列(十四)气泡图
上一篇: 多符号散点图 https://blog.csdn.net/zjw_python/article/details/98483989 下一篇: 雷达图 https://blog.csdn.net ...
最新文章
- zuul如果两个filter的order一样,是如何排序的?
- 工业物联网将借助新兴技术实现华丽转身
- vue - 减少打包后的体积
- Java基础-Eclipse第三方安装包管理工具之Maven
- python2中文导致的错误
- [翻译] python Tutorial 之一
- ajax跨域请求wcf,ajax wcf 指定某个域名 进行跨域访问
- android 编译模块
- Cesium教程系列汇总
- ENVI实验教程(6)实验六、遥感图像分类
- 用R命令看一下各个寄存器的设置情况
- 安装redis 5.0.5版本 真香警告
- 完成基于ICX285和ICX205两种CCD的兼容性电路设计
- 常见物联网操作系统介绍
- builder设计模式,写和很好
- CSS3鼠标悬停360度旋转效果
- 2018NOIp爆零记
- 电商领域知识图谱:常识抽取,表示与应用
- docker安装mysql 并挂载目录
- 郁 繁体为“鬰” 古同 “鬱”
热门文章
- python 图片自动分类机_用tensorflow神经网络实现一个简易的图片分类器
- 时间t与时间管理——柳比歇夫、德鲁…
- 【Mysql系列】MySQL创建数据库、CURD的操作
- linux驱动:二、LED灯驱动编写
- 第一个Java程序Hello World(IG牛逼)
- 绝地求生显示器测试软件,《绝地求生大逃杀》1728*1080分辨率怎么设置?自定义分辨率设置方法分享...
- Double与BigDecimal 比较
- 这套摄像头方案太适合监控院子了!(附:安装方法+物联卡推荐)
- Java集合框架详解
- 51单片机点亮LED灯以及实现2盏LED灯的交替闪烁