学习D3.js(四)堆叠柱状图

1,719 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

开始绘制

引入D3模块

  <!-- 选择器模块 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-selection@3"></script>

  <!-- 比例尺模块 和 依赖 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-array@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-color@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-format@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-interpolate@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-time@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-time-format@4"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-scale@4"></script>

  <!-- 坐标轴 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-axis@3"></script>

  <!-- 形状 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-path@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-shape@3"></script>

数据

  • 数据格式随意。后面使用时对格式不满意,可开始定义好格式或后面转换格式。
    /*
     * 绘制柱状图
     **/
    var bColor = ['#4385F4', '#34A853', '#FBBC05', '#E94335', '#01ACC2', '#AAACC2']
    var dataArr = [
      {
        label: '1月',
        value: 10.5,
        value2: 20.5
      },
      {
        label: '2月',
        value: 70.5,
        value2: 22.5
      },
      {
        label: '3月',
        value: 60.5,
        value2: 30.5
      },
      {
        label: '4月',
        value: 10.5,
        value2: 20.5
      },
      {
        label: '5月',
        value: 20.5,
        value2: 40.5
      },
      {
        label: '6月',
        value: 30.5,
        value2: 30.5
      }
    ]

添加画布

var svg = d3
  .select('.d3Chart')
  .append('svg')
  .attr('width', 500)
  .attr('height', 500)
  .style('background-color', '#1a3055')

创建比例尺

// 分段比例尺
var xScale = d3
  .scaleBand()
  .range([0, 400])
  .domain(dataArr.map((s) => s.label))
  .padding(0.4)
// 线性比例尺
var yScale = d3.scaleLinear().range([400, 0]).domain([0, 100])

绘制坐标轴

  • 根据定义好的比例尺,使用axis模块自动绘制坐标轴。
  • 绘制好坐标轴后,在坐标轴组中绘制文本标签。
var chart = svg.append('g').attr('transform', 'translate(50, 40)')
// X轴
chart.append('g').attr('class', 'xAxis').attr('transform', 'translate(15, 400)').call(d3.axisBottom(xScale))
// y轴
var makeYlines = () =>
  d3
    .axisLeft()
    .scale(yScale)
    .tickSize(-400)
    .tickFormat((d) => {
      return d + '%'
    })
chart.append('g').attr('class', 'yAxis').attr('transform', 'translate(15, 0)').call(makeYlines())
// 标签
d3.select('.xAxis')
  .append('text')
  .attr('x', 400 / 2 - 12)
  .attr('y', 0)
  .attr('dy', 45)
  .style('font-size', '24px')
  .text('日期')
// 标签
d3.select('.yAxis')
  .append('g')
  .attr('transform', 'translate(-40, 0)')
  .append('text')
  .attr('transform', 'rotate(-90)')
  .attr('class', 'axisText axisTextY')
  .style('font-size', '24px')
  .text('比例(%)')
d3.select('.axisTextY').attr('x', function () {
  return -200 + this.getBoundingClientRect().height / 2
})
d3.selectAll('.d3Chart text').style('fill', '#fff')
d3.selectAll('.d3Chart line').style('stroke', '#fff')
d3.selectAll('.d3Chart path').style('stroke', '#fff')

image.png

绘制柱状

  • 我们绘制的是堆叠柱状图,需要把同一类型的两个柱状组合在一起。
  • 在D3的d3-shape模块中,为我们准备好了工具d3.stack()组合堆叠。
// stack() 组合堆叠 配置
const stack = d3.stack().keys(['value', 'value2']).order(d3.stackOrderAscending)
.offset(d3.stackOffsetNone)

console.log(' stack', stack(dataArr))

image.png

  • 数据堆叠后,生成三维数组。三维数据中,0、1代表柱状的开始和结束高度。
// 柱状
const groups = chart.selectAll().data(stack(dataArr))
// 堆叠数据创建 堆
const heaps = groups
  .enter()
  .append('g')
  .attr('class', (d) => 'g ' + d.key)
  .attr('fill', (d, i) => bColor[i])
  • chart组上,绑定堆叠数据。
  • 生成新的g元素。这里只会生成两个,因为堆叠数据我们只定义了两类。
  • 使用堆叠类的key,为唯一标识。
// 堆叠数据 拆解 柱数据 绑定到对应柱上
const bars = heaps.selectAll().data((d) => {
  return d.map((item) => {
    item.index = d.index
    item.name = d.key
    return item
  })
})
// 在堆叠 中绘制柱
bars
  .enter()
  .append('rect')
  .attr('class', 'bar')
  .attr('x', (d) => xScale(d.data.label) + xScale.bandwidth() / 2 - 4)
  .attr('y', (d) => yScale(d[1]))
  .attr('width', xScale.bandwidth())
  .attr('height', (d) => yScale(d[0]) - yScale(d[1]))
  • 将二维数据中的类型标识和下标放入三维数据中,绑定数据到新对应的g元素上。
  • 最后就是正常的数组数据,开始设置柱状的样式和位置,就完成了柱状图。

image.png

  1. d3.stack() 启用堆叠布局生成器。
  2. .keys() 指定使用对象中的那些属性进行叠堆。
  3. .order() 指定叠堆内数据的排序方式。
  4. d3.stackOrderAscending() 排序函数,从小到大排序。

添加交互

var tooltips = d3
  .select('body')
  .append('div')
  .style('width', '100px')
  .style('height', '60px')
  .style('background-color', '#fff')
  .style('dispaly', 'flex')
  .style('justify-content', 'center')
  .style('padding', '10px')
  .style('border-radius', '5px')
  .style('opacity', 0)

d3.selectAll('.bar')
  .on('mouseenter', (e, g) => {
    tooltips
      .html(`月份:${g[`data`].label}<br /> 类型:${g['name']}<br /> 数据:${g[`data`][g['name']]}%`)
      .style('position', 'absolute')
      .style('left', `${e.clientX}px`)
      .style('top', `${e.clientY}px`)
      .style('opacity', 1)
  })
  .on('mouseleave', (e, g) => {
    tooltips.style('opacity', 0).style('left', `0px`).style('top', `0px`)
  })

4.gif

  • 创建提示框。通过标识符.bar获取所有的柱状。根据鼠标监听事件来设置对应的数据和位置。
  • 代码地址