上一篇: 基础折线图 https://blog.csdn.net/zjw_python/article/details/98210977

下一篇: 曲线图 https://blog.csdn.net/zjw_python/article/details/98478578

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

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

1 图表效果

2 数据

date,total,food,transportation,education
Mon,120,30,40,50
Tue,200,20,80,100
Wed,150,20,50,80
Thu,80,10,30,40
Fri,70,15,20,35
Sat,110,10,30,70
Sun,130,20,50,60

3 关键代码

导入数据

d3.csv('./data.csv', function(d){return {date: d.date,total: +d.total,food: +d.food,transportation: +d.transportation,education: +d.education};
}).then(function(data){.....

一些样式参数配置,与基础折线图类似

const config = {margins: {top: 80, left: 80, bottom: 50, right: 80},textColor: 'black',gridColor: 'gray',ShowGridX: [],ShowGridY: [50, 100, 150, 200, 250, 300, 350, 400],title: '堆叠面积图',pointSize: 5,pointColor: 'white',hoverColor: 'red',animateDuration: 1000}

尺度转换和布局函数定义,堆叠面积图和堆叠柱状图类似,都使用了d3.stack计算布局

/* ----------------------------尺度转换------------------------  */chart.scaleX = d3.scalePoint().domain(data.map((d) => d.date)).range([0, chart.getBodyWidth()])chart.scaleY = d3.scaleLinear().domain([0, (Math.floor((d3.max(data, (d) => d.total) + d3.max(data, (d) => d.food) + d3.max(data, (d) => d.education) + d3.max(data, (d) => d.transportation))/10) + 1)*10]).range([chart.getBodyHeight(), 0])chart.stack = d3.stack().keys(['total', 'food', 'transportation', 'education']).order(d3.stackOrderAscending).offset(d3.stackOffsetNone);

渲染线条,这里与基础折线图类似,使用插值和中间帧实现动画过渡效果

/* ----------------------------渲染线条------------------------  */chart.renderLines = function(){let lines = chart.body().selectAll('.line').data(chart.stack(data));lines.enter().append('path').attr('class', (d) => 'line line-' + d.key).merge(lines).attr('fill', 'none').attr('stroke', (d,i) => chart._colors(i)).transition().duration(config.animateDuration).attrTween('d', lineTween);lines.exit().remove();//中间帧函数function lineTween(_d){if (!_d) return;const generateLine = d3.line().x((d) => d[0]).y((d) => d[1]);const pointX = data.map((d) => chart.scaleX(d.date));const pointY = _d.map((d) => chart.scaleY(d[1]));const interpolate = getInterpolate(pointX, pointY);                const ponits = [];const interval = 1/(pointX.length-1);let index = 0;return function(t){if (t - interval > 0 && t % interval < Math.pow(10, -1.4)){  //保证线条一定经过数据点index = Math.floor(t / interval);ponits.push([pointX[index], pointY[index]]);}else{ponits.push([interpolate.x(t), interpolate.y(t)]);}return generateLine(ponits);}}//点插值function getInterpolate(pointX, pointY){const domain = d3.range(0, 1, 1/(pointX.length-1));domain.push(1);const interpolateX = d3.scaleLinear().domain(domain).range(pointX);const interpolateY = d3.scaleLinear().domain(domain).range(pointY);return {x: interpolateX,y: interpolateY};}}

渲染圆点,代码很简单

/* ----------------------------渲染点------------------------  */chart.renderPonits = function(){chart.stack(data).forEach((pointData, i) => {let ponits = chart.body().selectAll('.point-' + pointData.key).data(pointData);ponits.enter().append('circle').attr('class', 'point point-' + pointData.key).merge(ponits).attr('cx', (d) => chart.scaleX(d.data.date)).attr('cy', (d) => chart.scaleY(d[1])).attr('r', 0).attr('fill', config.pointColor).attr('stroke', chart._colors(i)).transition().duration(config.animateDuration).attr('r', config.pointSize).attr('value', (d) => pointData.key + ':' + d.data[pointData.key]);});};

渲染面,这里使用d3.area函数,方便地生成填充面,填充面地坐标使用stack函数计算地结果,为了和线条保持一致,仍然使用插值和中间帧实现动画过渡效果

chart.renderArea = function(){const areas = chart.body().insert('g',':first-child').selectAll('.area').data(chart.stack(data));areas.enter().append('path').attr('class', (d) => 'area area-' + d.key).merge(areas).style('fill', (d,i) => chart._colors(i)).transition().duration(config.animateDuration).attrTween('d', areaTween);//中间帧函数function areaTween(_d){if (!_d) return;const generateArea = d3.area().x((d) => d[0]).y0((d) => d[1]).y1((d) => d[2]);const pointX = data.map((d) => chart.scaleX(d.date));const pointY0 = _d.map((d) => chart.scaleY(d[0]));const pointY1 = _d.map((d) => chart.scaleY(d[1]));const interpolate = getAreaInterpolate(pointX, pointY0, pointY1);                const ponits = []return function(t){ponits.push([interpolate.x(t), interpolate.y0(t), interpolate.y1(t)]);return generateArea(ponits);}}//点插值function getAreaInterpolate(pointX, pointY0, pointY1){const domain = d3.range(0, 1, 1/(pointX.length-1));domain.push(1);const interpolateX = d3.scaleLinear().domain(domain).range(pointX);const interpolateY0 = d3.scaleLinear().domain(domain).range(pointY0);const interpolateY1 = d3.scaleLinear().domain(domain).range(pointY1);return {x: interpolateX,y0: interpolateY0,y1: interpolateY1};}}

接着渲染坐标轴,文本标签和网格线这些基础组件

/* ----------------------------渲染坐标轴------------------------  */chart.renderX = function(){chart.svg().insert('g','.body').attr('transform', 'translate(' + chart.bodyX() + ',' + (chart.bodyY() + chart.getBodyHeight()) + ')').attr('class', 'xAxis') .call(d3.axisBottom(chart.scaleX));}chart.renderY = function(){chart.svg().insert('g','.body').attr('transform', 'translate(' + chart.bodyX() + ',' + chart.bodyY() + ')').attr('class', 'yAxis').call(d3.axisLeft(chart.scaleY));}chart.renderAxis = function(){chart.renderX();chart.renderY();}/* ----------------------------渲染文本标签------------------------  */chart.renderText = function(){d3.select('.xAxis').append('text').attr('class', 'axisText').attr('x', chart.getBodyWidth()).attr('y', 0).attr('fill', config.textColor).attr('dy', 40).text('日期');d3.select('.yAxis').append('text').attr('class', 'axisText').attr('x', 0).attr('y', 0).attr('fill', config.textColor).attr('transform', 'rotate(-90)').attr('dy', -40).attr('text-anchor','end').text('每日收入(元)');}/* ----------------------------渲染网格线------------------------  */chart.renderGrid = function(){d3.selectAll('.yAxis .tick').each(function(d, i){if (config.ShowGridY.indexOf(d) > -1){d3.select(this).append('line').attr('class','grid').attr('stroke', config.gridColor).attr('x1', 0).attr('y1', 0).attr('x2', chart.getBodyWidth()).attr('y2', 0);}});d3.selectAll('.xAxis .tick').each(function(d, i){if (config.ShowGridX.indexOf(d) > -1){d3.select(this).append('line').attr('class','grid').attr('stroke', config.gridColor).attr('x1', 0).attr('y1', 0).attr('x2', 0).attr('y2', -chart.getBodyHeight());}});}

最后绑定鼠标交互事件,与基础折线图类似

/* ----------------------------绑定鼠标交互事件------------------------  */chart.addMouseOn = function(){//防抖函数function debounce(fn, time){let timeId = null;return function(){const context = this;const event = d3.event;timeId && clearTimeout(timeId)timeId = setTimeout(function(){d3.event = event;fn.apply(context, arguments);}, time);}}d3.selectAll('.point').on('mouseover', function(d){const e = d3.event;const position = d3.mouse(chart.svg().node());e.target.style.cursor = 'hand'd3.select(e.target).attr('fill', config.hoverColor);chart.svg().append('text').classed('tip', true).attr('x', position[0]+5).attr('y', position[1]).attr('fill', config.textColor).text(() => {return d3.select(this).attr('value');});}).on('mouseleave', function(){const e = d3.event;d3.select(e.target).attr('fill', config.pointColor);d3.select('.tip').remove();}).on('mousemove', debounce(function(){const position = d3.mouse(chart.svg().node());d3.select('.tip').attr('x', position[0]+5).attr('y', position[1]-5);}, 6));}

大功告成!!!


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

D3 二维图表的绘制系列(七)堆叠面积图相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. D3 二维图表的绘制系列(二十三)旭日图

    上一篇: 桑基图 https://blog.csdn.net/zjw_python/article/details/98611559 下一篇: 力导向图 https://blog.csdn.net/z ...

最新文章

  1. linux命令行模式连接网络,centos命令行模式连接无线网络的过程
  2. 电脑连接到网络显示未连接到服务器未响应,电脑dns服务器未响应怎么办?
  3. tar只解压tar包中某个文件
  4. 大数据 java 代码示例_功能Java示例 第7部分–将失败也视为数据
  5. 数据结构与算法之排序(归纳总结三)
  6. 《天下无贼》经典对白
  7. idea 查询项目代码行数_程序员的福利,四个技巧,让OneNote支持代码高亮
  8. [转]java 输出流转输入流
  9. Maya中操作普通DG节点和Dag节点的区别
  10. 机器学习基础(十五)—— blending
  11. scala学习手记15 - 独立对象和伴生对象
  12. 计算机科学管理学专业大学排名,2020管理科学专业大学排名
  13. R语言:SVD分解求解线性方程组AX=b
  14. sqlserver2000安装时提示挂起并重启
  15. DNS域名服务器的搭建
  16. Android 百分比布局库(percent-support-lib) 解析与扩展
  17. VR乒乓球项目Unity3D 开发经验整理,1总纲
  18. 2018年前端笔试高频题精选(二)
  19. Android studio 入门笔记
  20. Android Drawable 与 LayerList综合汇总

热门文章

  1. plt.xscale(‘log‘)含义
  2. sse——6位密码输入检测
  3. 好奇号火星车旅行日志January 25, 2017 – Wednesday火星日1591-1592车轮检查
  4. 如何利用GitHub发布个人网站
  5. 淘宝双11高并发架构是怎么设计的?看完这篇你就知道了!
  6. Javascript 两个对象数组 根据相同的id进行合并
  7. 关于硬盘写入读取的寿命的疑问(主要针对NAND Flash)
  8. 唐老师讲运算放大器(第二讲)——运放的供电电压VS、输入偏置电流IB、噪声VN,输入失调电压VOS,增益带宽积,压摆率
  9. MTBF、MTTR、MTTF
  10. 【读书笔记-思维导图】乔纳·莱勒-为什么大猩猩比专家高明