学习D3.js(十二)矩形树状图

988 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

开始绘制

引入D3模块

  • 引入整个D3模块。
<script src="https://d3js.org/d3.v7.min.js"></script>

数据

  • 自定义树状结构数据,用户绘图使用。
    const dataTree = {
      name: '太刀',
      children: [
        {
          name: '矿石',
          children: [
            {
              name: '结晶框',
              children: [
                { name: '蓝矿', num: 10 },
                { name: '黑铁矿', num: 3 },
                { name: '白灰矿', num: 4 }
              ]
            }
          ]
        },
        {
          name: '木材',
          children: [
            {
              name: '稀木',
              children: [
                { name: '钴木', num: 4 },
                { name: '黑木', num: 2 }
              ]
            },
            {
              name: '水木',
              children: [{ name: '蓝木', num: 4 }]
            }
          ]
        },
        {
          name: '宝石',
          children: [
            {
              name: '太阳类',
              children: [
                { name: '日金石', num: 6 },
                { name: '熔岩石', num: 1 }
              ]
            },
            {
              name: '深海类',
              children: [
                { name: '寒铁石', num: 2 },
                { name: '金晶石', num: 3 },
                { name: '玄冰结晶', num: 2 }
              ]
            }
          ]
        }
      ]
    }

添加画布

  • 初始化画布。
    var width = 500
    var height = 500
    var margin = 30
    var svg = d3
      .select('.d3Chart')
      .append('svg')
      .attr('width', width)
      .attr('height', height)
      .style('background-color', '#1a3055')
    // 图
    var chart = svg.append('g').attr('transform', `translate(${margin}, ${margin})`)

创建格式转换器

// 生成层次结构数据 - 并为各个节点指定 层次结构数据
const rootTree = d3
  .hierarchy(dataTree)
  .sum((d) => d.num) // 计算绘图属性value的值  -求和 其子节点所有.num属性的和值
  .sort((a, b) => a.value - b.value) // 根据 上面计算出的 value属性 排序
  
console.log('🚀 ~ file: 学习D3.js(十二)矩形树状图.html ~ line 88 ~ rootTree', rootTree)

image.png

  1. d3.hierarchy() 根据指定的层次结构数据构造一个根节点,并为每个节点创建层级属性。
  2. .sum() 给每个节点添加value属性。当前节点的 value 值和所有后代的 value 的合计。
  3. .sort() 对每级节点排序排序。从大到小排序。
  • 使用D3提供的.hierarchy()生成带有层级属性的数据。
const TreeMap = d3
  .treemap()
  .size([width - 2 * margin, height - 2 * margin])
  .round(true)
  .padding(1)(rootTree)
console.log('🚀 ~ file: 学习D3.js(十二)矩形树状图.html ~ line 99 ~ TreeMap', TreeMap)

image.png

  1. d3.treemap() 根据每个节点的值递归的将区域划分为矩形。
  2. .size() 二元数值数组,设置矩形的宽高。
  3. .round() 启用或禁用四舍五入。
  4. .padding() 设置矩形布局之间的间隔。
  • 使用D3提供的.treemap()计算出每个节点矩形数据x0、y0\x1、y1
var colorScale = d3.scaleOrdinal(d3.schemeSet3)
  1. d3.scaleOrdinal() 用于创建和返回具有指定范围和域的序数标度。
  2. d3.schemeSet3 d3内部定义的颜色数组。
  • 通过不同的传入值,依次获取颜色数组中的值。

绘制矩形

const rectChart = chart.append('g')
  • 创建矩形绘制组g
    rectChart
      .selectAll()
      .data(rootTree.leaves())
      .enter()
      .append('rect')
      .attr('class', 'cell')
      .attr('x', (d) => d.x0)
      .attr('y', (d) => d.y0)
      .attr('width', (d) => d.x1 - d.x0)
      .attr('height', (d) => d.y1 - d.y0)
      .attr('fill', (d, i) => colorScale(i))

image.png

  1. .leaves() 获取所有叶子节点的数据。
  • 矩形树状图只展示叶子节点,所以使用rootTree.leaves()获取所有叶子节点的数据。
  • 通过矩形数据绘制出每个叶子节点。
rectChart
  .selectAll()
  .data(rootTree.leaves())
  .enter()
  .append('text')
  .attr('class', 'cellText')
  .attr('transform', (d) => 'translate(' + (d.x0 + d.x1) / 2 + ',' + ((d.y0 + d.y1) / 2 + 6) + ')')
  .attr('fill', '#555555')
  .attr('text-anchor', 'middle')
  .text((d) => d.data.name)
  .text(function (d) {
    if (textWidthIsOk(d, this)) {
      return d.data.name
    } else {
      return '...'
    }
  })
// 检测文本长度是否合适
function textWidthIsOk(d, text) {
  const textWidth = text.getBBox().width
  if (d.x1 - d.x0 >= textWidth) return true
  return false
}

image.png

  1. .getBBox() DOM对象中的方法。返回元素的边界框描述。
  • 同样的绑定所有叶子节点数据。
  • 文本位置信息特殊处理,移动到矩形中间。
  • 对文本设置了,两次值。第一次是为了获取文本的宽度,判断是否超出矩形。超出修改为...。

添加交互

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

    d3.selectAll('.cell,.cellText')
      .on('mouseover', function (e, d) {
        tooltips
          .html(`材料:${d.data.name}<br /> 数量:${d.value}`)
          .style('position', 'absolute')
          .style('left', `${e.clientX}px`)
          .style('top', `${e.clientY}px`)
          .style('opacity', 1)
      })
      .on('mouseleave', function (e, d) {
        tooltips.style('opacity', 0).style('left', `0px`).style('top', `0px`)
      })
  • 创建提示框。
  • 在鼠标事件监听函数中,控制提示框的内容和是否展示。
  • 代码地址

3.gif