学习D3.js(二十一)平行坐标系

434 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

引入D3模块

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

数据

// 坐标
const names = ['生命', '力量', '耐力', '速度', '魔力']
// 数据
const dataArr = [
  {
    name: '张三',
    a1: '90',
    a2: '80',
    a3: '20',
    a4: '20',
    a5: '5'
  },
  {
    name: '李四',
    a1: '50',
    a2: '5',
    a3: '20',
    a4: '10',
    a5: '95'
  },
  {
    name: '王五',
    a1: '50',
    a2: '60',
    a3: '50',
    a4: '60',
    a5: '50'
  }
]
  • 格式不是唯一的,按自己习惯的格式设计。

添加画布

// 画布
const width = 500
const height = 500
const margin = 20
const svg = d3
  .select('.d3Chart')
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .style('background-color', '#1a3055')
// 图
const chart = svg.append('g').attr('transform', `translate(${margin}, ${margin})`)

比例尺和配置信息

// 创建颜色比例尺 
const colorScale = d3.scaleOrdinal(d3.quantize(d3.interpolateRainbow, dataArr.length + 1))
  • 颜色比例尺。
const scale = d3
      .scaleLinear()
      .domain([0, 100])
      .range([height - margin * 3, 0])
  • 线性比例尺,这里每个轴使用的时同一个。如需不同轴使用不同比例尺,循环保存,后面使用下标获取。

绘制坐标轴和折线

const axiss = chart.append('g').selectAll().data(names)
axiss
  .enter()
  .append('g')
  .attr('class', (d, i) => 'axis axis-' + i)
  .attr('transform', (d, i) => 'translate(' + ((i + 0.5) * (height - margin * 2)) / 5 + ',0)')
  .call(d3.axisLeft(scale).ticks(10))
  .append('text')
  .attr('dx', '0.5em')
  .attr('transform', (d, i) => 'translate(0,' + (height - margin * 2) + ')')
  .text((d) => d)

d3.selectAll('.d3Chart text').style('fill', '#fff')
d3.selectAll('.d3Chart line').style('stroke', '#fff')
d3.selectAll('.d3Chart path').style('stroke', '#fff')
  • 创建绘制组,绑定数据。对每个数据创建绘制组,绘制坐标轴和文本。
  • 设置坐标轴和文本的颜色。

image.png

const lines = chart.append('g').selectAll().data(dataArr)
const line = lines.enter().append('g').attr('class', 'line')
  • 创建线绘制组,绑定数据。对每个数据创建绘制组,并设置唯一标识。
line
  .append('path')
  .attr('class', (d, i) => 'line line-' + d.name)
  .attr('stroke', (d, i) => colorScale(d.name))
  .attr('stroke-width', 2)
  .attr('fill', 'none')
  .attr('d', (d) => d3.line()(generatePoints(d)))

/**
 * 折线点计算
 * */
function generatePoints(d) {
  const arr = names.map((item, i) => {
    return [((i + 0.5) * (height - margin * 2)) / 5, scale(d[`a${i + 1}`])]
  })
  return arr
}
  • 创建折线点计算函数,绘制折线。
line
  .append('text')
  .attr('class', (d, i) => 'lineText lineText-' + i)
  .attr('dx', '0.5em')
  .attr('fill', (d, i) => colorScale(d.name))
  .attr('style', 'cursor: pointer;')
  .attr('transform', (d, i) => 'translate(' + ((4 + 0.5) * (height - margin * 2)) / 5 + ',' + scale(d['a5']) + ')')
  .text((d) => d.name)

image.png

  • 绘制文本并设置唯一标识。这里文本是绘制在结束位置,一共5条轴,所以是((4 + 0.5) * (height - margin * 2)) / 5 + ',' + scale(d['a5'])

交互

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

d3.selectAll('.lineText')
  .on('mouseenter', (e, d) => {
    tooltips
      .html(
        `生命:${d.a1}
        <br /> 力量:${d.a2}
        <br /> 耐力:${d.a3}
        <br /> 速度:${d.a4}
        <br /> 魔力:${d.a5}`
      )
      .style('position', 'absolute')
      .style('left', `${e.clientX - 120}px`)
      .style('top', `${e.clientY}px`)
      .style('opacity', 1)

    d3.select(`.line-${d.name}`).attr('stroke-width', '5')
  })
  .on('mouseleave', (e, d) => {
    tooltips.style('opacity', 0).style('left', `0px`).style('top', `0px`)

    d3.select(`.line-${d.name}`).attr('stroke-width', '2')
  })

2.gif

  • 创建提示框,监听鼠标移入移出事件。移入,修改DOM对象的文本和样式。移出,重置元素。