D3.js 绘制柱状图

使用D3 V4版本绘制

使用D3绘制柱状图,绘制效果如下:

柱状图效果

使用D3绘制柱状图,先对需要绘制的图形拆解,主要分为以下几个部分:
1. 比例尺的定义
2. 坐标轴的绘制
3. 添加矩形
4. 修改坐标轴样式
5. 柱状图装饰效果添加


首先,先定义data

this.graphData = [
  { id:1, name: '数据一', value: 2345 },
  { id:2, name: '数据二', value: 4467 },
  { id:3, name: '数据三', value: 2356 },
  { id:4, name: '数据四', value: 3895 },
  { id:5, name: '数据五', value: 3495 },
  { id:6, name: '数据六', value: 3895 },
  { id:7, name: '数据七', value: 2597 },
  { id:8, name: '数据八', value: 2997 },
];

柱状图X轴展示的数据:

const xData = dataset.map((item) => item.name);

绘制过程如下:

添加画布
首先,图形要添加到画布上:

const width = 650;
const height = 420;
const svg = d3.select(node).append('svg')
    .attr('width', width)
    .attr('height', height);

定义比例尺

const xScale = d3.scaleBand()
    .domain(xData)
    .rangeRound([0, width - paddingLeft - paddingRight])
const yScale = d3.scaleLinear()
    .domain([0, max * 1.5])
    .range([height - paddingTop - paddingBottom, 0]);

scale定义方式不同,则使用方式也是不同。此处xData为离散型数据,对应连续区间,所以使用d3.scaleBand();

关于几种常用比例尺的区别:

1. d3.scaleLinear() 线性比例尺
domain:连续型
range:连续型
使用: scale_l = d3.scaleLinear().domain([1,10]).range([0,100])
2. d3.scaleOrdinal() 序数比例尺
domain: 离散型
range:离散型
可以简单理解为map映射
使用: scale_o = d3.scaleOrdinal().domain(['a', 'b', 'c']).range([10, 20, 30])
3. d3.scaleBand() 序数比例尺
domain: 离散型
range:连续型
可以理解为用domain将range平均分割
使用: scale_b= d3.scaleBand().domain([1,2,3,4]).range([0,100])
4. d3.scalePoint() 序数比例尺
domain: 离散型
range:连续型
与scaleBand()不同的地方在于带宽为零
使用: scale_p= d3.scalePoint().domain([1,2,3,4]).range([0,100])

关于 Band Scales :
Band Scales
关于 Point Scales :
Point Scales

添加坐标轴

// 定义X坐标轴
const xAxis = d3.axisBottom(xScale)
   .ticks(0)
   .tickPadding(12);
// 定义Y坐标轴
const yAxis = d3.axisLeft(yScaleAxis)
   .ticks(5)
   .tickPadding(8)
   .tickFormat(d3.format('d'));
// 添加
svg.append('g')
   .attr('class', 'r-xAxis')
   .attr('transform', `translate(${paddingLeft},${height - paddingBottom})`)
   .call(xAxis);
svg.append('g')
   .attr('class', 'r-yAxis')
   .attr('transform', `translate(${paddingLeft},${paddingTop})`)
   .call(yAxis);

svg 坐标轴原点 (0, 0) 在左上角, y 坐标轴最大值在上方,最小值展示在下方,故yScale domain([0, max * 1.5]) 对应 range([height - paddingTop - paddingBottom, 0]);

y 轴使用了d3.tickFormat,使用请参考 d3.format

添加矩形

yScale.range([0, height - paddingTop - paddingBottom]);

const rectGroup = svg.selectAll('.rectItem')
  .data(dataset)
  .enter()
  .append('g')
  .attr('class', 'rectItem');
rectGroup.append('rect')
  .attr('width', rectWidth)
  .attr('height', (d) => yScale(d.value))
  .attr('y', (d) => height - paddingTop - paddingBottom - yScale(d.value))
  .attr('fill', 'url(#rbGraphsColor)')
  .attr('transform', `translate(${paddingLeft},${paddingTop})`)
  .attr('x', (d) => xScale(d.name) + ((xScale.bandwidth() - rectWidth) / 2));

添加矩形后效果展示

添加矩形后效果展示
设置坐标轴样式

关于坐标轴的构成

<g>
	<!-- 第一个刻度 -->
	<g>
		<line></line>   <!-- 第一个刻度的直线 -->
		<text></text>   <!-- 第一个刻度的文字 -->
	</g>
	<!-- 第二个刻度 -->
	<g>
		<line></line>   <!-- 第二个刻度的直线 -->
		<text></text>   <!-- 第二个刻度的文字 -->
	</g> 
	...
	<!-- 坐标轴的轴线 -->
	<path></path>
</g>

本例中css样式设置:

.r-xAxis, .r-yAxis {
	line {
     	display: none;
    }
    path {
      stroke: #393c45;
    }
    text {
      font-size: 14px;
    }
}

其他

  • 添加柱状图后面网格线
  • 添加数据文字
  • 添加柱状图装饰效果
  • 添加动效

此处只介绍如何添加网格线

svg.selectAll('.r-yAxis .tick')
   .append('line')
   .attr('class', 'row-grid')
   .attr('x1', 0)
   .attr('x2', width - paddingLeft - paddingRight)
   .attr('y1', 0)
   .attr('y2', 0);
 .row-grid {
   display: block;
   stroke: #b4b5b5;
   /* shape-rendering: crispedges;*/
   stroke-width: 1;
   opacity: 0.7;
 }

柱状图添加网格线

最后,附上完整代码:

JS

import * as d3 from 'd3';
export default function graphs(node, dataset) {
    (() => {
        d3.select(node).selectAll('svg').remove();
    })();

    // ***************数据初始化*******************
    const width = node.offsetWidth;
    const height = node.offsetHeight;
    const paddingTop = 60;
    const paddingBottom = 98;
    const paddingLeft = 90;
    const paddingRight = 24;
    const rectWidth = 40;
    const decoRectWidth = 2;
    const delay = 0;
    const duration = 2000;
    const max = Math.max(...dataset.map((item) => item.value));

    const xData = dataset.map((item) => item.name); // 对接数据时根据name名创建

    /****************************** 比例尺 ***************************************/
    const xScale = d3.scaleBand()
        .domain(xData)
        .rangeRound([0, width - paddingLeft - paddingRight])
    const yScale = d3.scaleLinear()
        .domain([0, max * 1.5])
        .range([height - paddingTop - paddingBottom, 0]);

    // 绘制
    const svg = d3.select(node).append('svg')
        .attr('width', width)
        .attr('height', height);
    // 坐标轴
    const xAxis = d3.axisBottom(xScale)
        .ticks(0)
        .tickPadding(12);
    const yAxis = d3.axisLeft(yScale)
        .ticks(5)
        .tickPadding(8)
        .tickFormat(d3.format('d'));
    // ***************坐标轴***************
    svg.append('g')
        .attr('class', 'r-xAxis')
        .attr('transform', `translate(${paddingLeft},${height - paddingBottom})`)
        .call(xAxis);

    svg.append('g')
        .attr('class', 'r-yAxis')
        .attr('transform', `translate(${paddingLeft},${paddingTop})`)
        .call(yAxis);
    // 添加横向网格线
    svg.selectAll('.r-yAxis .tick')
        .append('line')
        .attr('class', 'row-grid')
        .attr('x1', 0)
        .attr('x2', width - paddingLeft - paddingRight)
        .attr('y1', 0)
        .attr('y2', 0);

    // ***************矩形图******************
    yScale.range([0, height - paddingTop - paddingBottom]);

    const rectGroup = svg.selectAll('.rectItem')
        .data(dataset)
        .enter()
        .append('g')
        .attr('class', 'rectItem');

    rectGroup.append('rect')
        .attr('width', rectWidth)
        .attr('height', 0)
        .attr('y', height - paddingTop - paddingBottom)
        .attr('fill', 'url(#rbGraphsColor)')
        .attr('transform', `translate(${paddingLeft},${paddingTop})`)
        .attr('x', (d) => xScale(d.name) + ((xScale.bandwidth() - rectWidth) / 2))
        .transition()
        .delay(delay)
        .duration(duration)
        .attr('height', (d) => yScale(d.value))
        .attr('y', (d) => height - paddingTop - paddingBottom - yScale(d.value));
    // 中间装饰矩形
    rectGroup.append('rect')
        .attr('width', decoRectWidth)
        .attr('height', 0)
        .attr('y', height - paddingTop - paddingBottom)
        .attr('fill', '#30ca6e')
        .attr('opacity', 0.6)
        .attr('transform', `translate(${paddingLeft},${paddingTop})`)
        .attr('x', (d) => xScale(d.name) + ((xScale.bandwidth() - decoRectWidth) / 2))
        .transition()
        .delay(delay)
        .duration(duration)
        .attr('height', (d) => yScale(d.value))
        .attr('y', (d) => height - paddingTop - paddingBottom - yScale(d.value));
    // 上方装饰矩形
    rectGroup.append('rect')
        .attr('width', rectWidth)
        .attr('height', 2)
        .attr('y', height - paddingTop - paddingBottom - 4)
        .attr('fill', '#32dd77')
        .attr('transform', `translate(${paddingLeft},${paddingTop})`)
        .attr('x', (d) => xScale(d.name) + ((xScale.bandwidth() - rectWidth) / 2))
        .transition()
        .delay(delay)
        .duration(duration)
        .attr('y', (d) => height - paddingTop - paddingBottom - yScale(d.value));

    rectGroup.append('text')
        .text((d) => d.value)
        .attr('y', height - paddingTop - paddingBottom)
        .attr('class', 'valueDes')
        .attr('transform', `translate(${20},${paddingTop - 5})`)
        .attr('x', (d) => xScale(d.name) + ((xScale.bandwidth() - rectWidth) / 2) + paddingLeft)
        .transition()
        .delay(delay)
        .duration(duration)
        .attr('y', (d) => height - paddingTop - paddingBottom - yScale(d.value));

    const xyLine = svg.append('g')
        .attr('class', 'lineGroup');
    // x
    xyLine.append('line')
        .attr('class', 'xyLine')
        .attr('x1', paddingLeft - 1)
        .attr('x2', width - paddingRight)
        .attr('y1', height - paddingBottom)
        .attr('y2', height - paddingBottom);
    // y
    xyLine.append('line')
        .attr('class', 'xyLine')
        .attr('x1', paddingLeft)
        .attr('x2', paddingLeft)
        .attr('y1', paddingTop)
        .attr('y2', (height - paddingBottom) + 1);
}

CSS

svg {
  .valueDes {
    text-anchor: middle;
    font-size: 14px;
    fill: #c4c4c4;
  }
  .r-xAxis {
    path, line {
      display: none;
    }
    text {
      font-size: 14px;
      color: #fdfdfd;
    }
  }
  .r-yAxis {
    path, line {
      display: none;
    }
    text {
      font-size: 14px;
      color: #c4c4c4;
    }
    .row-grid {
      display: block;
      stroke: #393c45;
      stroke-width: 1;
      opacity: 0.7;
    }
  }
  .xyLine {
    stroke: #393c45;
    stroke-width: 1;
  }
}
参考文章 :
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对比柱状图可以使用d3.js中的矩形图(rect)元素来绘制。以下是一个简单的例子: HTML代码: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js对比柱状图</title> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <svg id="chart" width="400" height="300"></svg> <script src="script.js"></script> </body> </html> ``` JavaScript代码: ```javascript // 数据 var data = [ {name: 'A', value: 20}, {name: 'B', value: 35}, {name: 'C', value: 10}, {name: 'D', value: 15}, {name: 'E', value: 25} ]; // 定义svg画布大小 var width = 400; var height = 300; // 定义比例尺 var xScale = d3.scaleBand() .domain(data.map(function(d) { return d.name; })) .range([0, width]) .padding(0.1); var yScale = d3.scaleLinear() .domain([0, d3.max(data, function(d) { return d.value; })]) .range([height, 0]); // 创建svg元素 var svg = d3.select('#chart') .append('g') .attr('transform', 'translate(' + 50 + ',' + 10 + ')'); // 绘制矩形 svg.selectAll('rect') .data(data) .enter() .append('rect') .attr('x', function(d) { return xScale(d.name); }) .attr('y', function(d) { return yScale(d.value); }) .attr('width', xScale.bandwidth()) .attr('height', function(d) { return height - yScale(d.value); }) .style('fill', 'steelblue'); ``` 解释: 1. 定义数据,包含每个柱子的名称和值。 2. 定义画布大小。 3. 定义x轴和y轴的比例尺,x轴比例尺使用`scaleBand`,y轴比例尺使用`scaleLinear`。 4. 创建svg元素,并将其偏移50像素到右侧和10像素到下方。 5. 绘制矩形,使用`selectAll`和`data`方法绑定数据,使用`enter`方法进入数据,使用`append`方法添加矩形元素,使用`attr`方法设置x、y、width和height属性,使用`style`方法设置颜色。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值