柱状堆积图

项目地址
使用 D3.js 创建的图表:

  • 使用 D3.js 创建根据值域颜色渐变的地图
  • D3.js 中动态计算 x 轴 y 轴的宽度以及偏移量
  • 在 Ember.js 项目中由浅入深使用 D3.js 绘制图表

8.1 效果图

可以看到每组数据都进行了叠加。

现在来看一下具体实现:

8.2 实现

堆叠图虽然和柱状图在展示上相差不是很多,但是在实现上差距还是有的。简单的柱状图是使用 svg 中的 rect 元素,根据数据,赋予 rect 相应的宽高来展示数据的差异。但是堆叠图是使用多个 rect 堆叠起,其中的 rect 关系我们是需要计算的。还好在 D3.js 中提供了相关的 API: d3.shape 中的 d3.stack ,对数据进行处理。

本次示例中也是使用的官方示例数据。

8.2.1 坐标轴

坐标轴的生成在之前的文章中也提到不少,这里为了尝试更多的 scale ,使用了在本例中不太合适的 scaleTime 比例尺。

注意 比例尺的选择要根据展示的图的 nature 来选择合适的比例尺,不能只是淡淡因为 x 轴 lable 的数据是什么类型就选择什么类型的比例尺。

看到这里,应该发现我们的展示图上有一些问题:柱状图并不是居中在坐标的 ticks 中,而是有一个 1/2 bandWidth 的偏移。这也是 scaleTime 的原因。

上代码

/** 数据格式* [{month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},{month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},{month: new Date(2015, 2, 1), apples:  640, bananas:  960, cherries: 640, dates: 400},{month: new Date(2015, 3, 1), apples:  320, bananas:  480, cherries: 640, dates: 400}]*/
// ...
const timeDate = data.map(datum => datum.month)
// y 轴 scale
const yScale = scaleLinear().domain([0, max(series, d => max(d, d => d[1]))]).range([this.height - padding.pt - padding.pb, 0]);const yAxis = axisLeft(yScale)svg.append('g').classed("y-axis", true).call(yAxis);// y轴宽度
const yAxisWidth: number = getYAxisWidth(svg.select('.y-axis'))
svg.select(".y-axis").attr("transform", `translate(${padding.pl + yAxisWidth},${padding.pt})`);// 为了给两端留出空白区域
const phMinDate = timeMonth.offset(min(timeDate),-1);
const phMaxDate = timeMonth.offset(max(timeDate),1);// x轴scale
const xScale = scaleTime()
.domain([min(timeDate),max(timeDate)])
.range([padding.pl, this.width - padding.pr - yAxisWidth]);// x轴
const xAxis = axisBottom(xScale)
.ticks(timeMonth.every(1))
.tickFormat(timeFormat('%y-%m'))svg.append('g').classed("x-axis", true).attr("transform", `translate(${yAxisWidth},${this.height - padding.pb})`).call(xAxis)
// ...

其中使用的 utils 函数 - getYAxisWidth 在 项目 可查看。

这个示例展示了 scaleTime 的用法以及对时间的进一步格式化:

// x轴
const xAxis = axisBottom(xScale)
.ticks(timeMonth.every(1))
.tickFormat(timeFormat('%y-%m'))

timeMonth.every 的参数值为 3 的时候,我们就可以得到按季度分隔的时间轴了。

这时我们可以看到:


两个坐标轴已然准备就绪。

8.2.2 数据展示

要展示 stack 数据,需要对原始数据进行预处理。D3.js 提供 d3.stack 处理函数:

const stackIns = stack().keys(Object.keys(data[0]).slice(1)).order(stackOrderNone).offset(stackOffsetNone);const series = stackIns(data);

现在,我们得到的 series 即时我们要绘制的堆叠图的能够识别的数据:

svg.selectAll('g.stack').data(series).join(enter => enter.append('g'),update => update,exit => exit.remove()).classed('stack', true).attr('fill', (d:any,i:number)=>schemePaired[i]).attr('transform',`translate(${yAxisWidth},${padding.pt})`).selectAll('rect').data(d => d).join(enter => enter.append('rect'),update => update,exit => exit.remove()
).attr('x', (d: any) => xScale(d.data.month)).attr('y', (d: any) => yScale(d[1])).attr('height', (d: any) => yScale(d[0]) - yScale(d[1])).attr('width', 14)

这样像图 8.1 的效果就出来了。现在来看一下完整的代码:

// bp-stack.tsimport Component from '@glimmer/component';
import { action } from '@ember/object';
import Layout from 'ember-d3-demo/utils/d3/layout';
import { scaleTime, scaleLinear } from 'd3-scale';
import { axisBottom, axisLeft } from 'd3-axis';
import { min, max } from 'd3-array';
import { timeMonth } from 'd3-time';
import { timeFormat } from 'd3-time-format';
import { stack, stackOrderNone, stackOffsetNone } from 'd3-shape';
import { getYAxisWidth } from 'ember-d3-demo/utils/d3/yAxisWidth';
import { schemePaired} from 'd3-scale-chromatic';interface D3BpStackArgs {data: any[];/** 数据格式* [{month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},{month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},{month: new Date(2015, 2, 1), apples:  640, bananas:  960, cherries: 640, dates: 400},{month: new Date(2015, 3, 1), apples:  320, bananas:  480, cherries: 640, dates: 400}]*/width: number;height: number;
}export default class D3BpStack extends Component<D3BpStackArgs> {constainer: any = nullwidth: number = this.args.widthheight: number = this.args.height@actioninitChart() {const data = this.args.datalet layout = new Layout('.bp-stack')let { width, height } = thisif (width) {layout.setWidth(width)} else {width = layout.getWidth()}if (height) {layout.setHeight(height)} else {height = layout.getHeight()}const container = layout.getContainer()this.width = layout.getWidth()this.height = layout.getHeight()this.constainer = containerconst padding = layout.getPadding()// 生成 svglet svg = container.append('svg').attr("width", width).attr("height", height);const stackIns = stack().keys(Object.keys(data[0]).slice(1)).order(stackOrderNone).offset(stackOffsetNone);const series = stackIns(data);const timeDate = data.map(datum => datum.month)// y 轴 scaleconst yScale = scaleLinear().domain([0, max(series, d => max(d, d => d[1]))]).range([this.height - padding.pt - padding.pb, 0]);const yAxis = axisLeft(yScale)svg.append('g').classed("y-axis", true).call(yAxis);// y轴宽度const yAxisWidth: number = getYAxisWidth(svg.select('.y-axis'))svg.select(".y-axis").attr("transform", `translate(${padding.pl + yAxisWidth},${padding.pt})`);// 为了给两端留出空白区域const phMinDate = timeMonth.offset(min(timeDate),-1);const phMaxDate = timeMonth.offset(max(timeDate),1);// x轴scaleconst xScale = scaleTime().domain([min(timeDate),max(timeDate)]).range([padding.pl, this.width - padding.pr - yAxisWidth]);// x轴const xAxis = axisBottom(xScale).ticks(timeMonth.every(1)).tickFormat(timeFormat('%y-%m'))svg.append('g').classed("x-axis", true).attr("transform", `translate(${yAxisWidth},${this.height - padding.pb})`).call(xAxis)svg.selectAll('g.stack').data(series).join(enter => enter.append('g'),update => update,exit => exit.remove()).classed('stack', true).attr('fill', (d:any,i:number)=>schemePaired[i]).attr('transform',`translate(${yAxisWidth},${padding.pt})`).selectAll('rect').data(d => d).join(enter => enter.append('rect'),update => update,exit => exit.remove()).attr('x', (d: any) => xScale(d.data.month)).attr('y', (d: any) => yScale(d[1])).attr('height', (d: any) => yScale(d[0]) - yScale(d[1])).attr('width', 14)}
}
{{!-- bp-stack.hbs --}}
<div class="bp-stack" {{did-insert this.initChart}}></div>

使用 D3.js 创建柱状堆积图相关推荐

  1. label mpchart 饼图_Origin系列:绘制柱状堆积图

    原创不易,感谢分享,欢迎转发,请点在看 堆积柱状图十分美观,不仅能够展示数据占比,更能表现其变化趋势,是科研必备技能 今天分享粉丝提出类似下列图形用Origin绘制多列柱状堆积图.希望对大家有所帮助 ...

  2. 【python代码实现带数据柱状堆积图】

    import numpy as np import matplotlib.pyplot as pltcategory_names = ['中性', '消极','积极'] results = {'top ...

  3. 柱状折线图2-双柱状重合堆积折线-重写图例点击事件

    本例子: 使用了formatter方法重写了提示层的展示数据 使用了双x轴实现重合 使用了stack实现堆积 使用了legendselectchanged和dispatchAction重写了图例点击事 ...

  4. d3.js v5 饼状图(加载动画、悬浮动画、图注以及悬浮提示框)

    实现的效果大概是这样: 实现代码如下: <html><body></body><style>div{background: #F2F4FF;width: ...

  5. d3.js折线图_学习使用D3.js创建折线图

    d3.js折线图 by Sohaib Nehal 通过Sohaib Nehal 学习使用D3.js创建折线图 (Learn to create a line chart using D3.js) 使用 ...

  6. SwiftUI之深入解析如何使用组合矩形GeometryReader创建条形(柱状)图

    一.图表布局 条形(柱状)图以矩形条的形式呈现数据的类别,其宽度和高度与它们表示的值成比例.SwiftUI 对探索不同布局和预览实时视图结果是很友好的,很容易将部分内容提取到子视图中,以便每个部分都很 ...

  7. django Echarts画柱状推移图

    1. 首先确定要画什么样的图,在Echarts官网找好案例 2.根据图确认需要准备的数据,从后台准备数据传递给模板 3.模板渲染,使用Echarts组件功能完成自己想要的内容 中间遇到几个坑: 1. ...

  8. 如何展现两极化数据,Excel柱状断层图不二之选

    点赞再看,养成习惯:至长反短,至短反长. 微信搜索[亦心Excel]关注这个不一样的自媒体人. 本文 GitHub https://github.com/hugogoos/Excel 已收录,包含Ex ...

  9. exlc如何对比_excel表格图形数据比较-Excel如何做柱状对比图

    excel表格怎么做数据对比图 Excel的图表功能已供了柱状对,下面以Excel 2010为例进行实例演示--用柱状图对比显示下面数据列一和系列二 1.选中数据→插入→柱形图→簇状柱形图 2.因为数 ...

最新文章

  1. 学号 20172326 《程序设计与数据结构》第三周学习总结
  2. 大凉山的美术课,怎么就跟英特尔扯上关系了
  3. go语言基础到提高(1)-hello,world
  4. 用友u8cloud使用教程_四大ERP供应商SAP、Oracle、用友、金蝶哪个好
  5. 《C++ Primer》1.51节练习
  6. shell脚本启动kafka集群的多台节点
  7. 长路漫漫,唯剑作伴--loadView、viewDidLoad及viewDidUnload的关系
  8. android jni release,Android NDK 设置编译模式debug和release
  9. C++函数的返回值是指针
  10. MySQL备份还原知识要点
  11. idea导入java项目步骤_idea导入javaweb项目
  12. sap代加工流程图_委外加工_SAP的两种典型委外处理方法
  13. 利用矩阵特征值求解多项式的根
  14. c语言学习-1-Visual Studio 2019下载和安装
  15. 一键快速打开IE的Internet选项->连接->局域网设置
  16. 卧槽,javaCV不到十行代码实现图片OCR文字识别
  17. asp.net mvc 5 identity 2.0 注册时密码强度验证
  18. 树以及二叉树的常用性质以及遍历
  19. 苹果7手机通讯录删除了还能够找回来么
  20. 51单片机之STC89C52RC最小系统板烧录说明

热门文章

  1. 【AI】AI学习目录汇总
  2. 玩转linux 这些命令就够了
  3. 外汇/本币;结算/清算
  4. 组合和聚合举例说明_聚合与组合的区别
  5. 一级MS Office 复习资料
  6. 【Python】:好好学习天天向上实例
  7. K8S报no kind Deployment is registered for version apps/v1和pod status为ContainerCreating
  8. 基于OpenCASCADE自制三维建模软件(十一)使用ASSIMP导入导出
  9. 计算机新手技巧,绝对实用 电脑操作技巧60招大放送
  10. 用QQ邮箱注册到MSN live 账号