D3.js v5.0 弦图

D3.js v5.0 弦图_第1张图片
数据结构:

[
  {
    "name": "flare.analytics.cluster.AgglomerativeCluster",
    "size": 3938,
    "imports": [
      "flare.animate.Transitioner",
      "flare.vis.data.DataList",
      "flare.util.math.IMatrix",
      "flare.analytics.cluster.MergeEdge",
      "flare.analytics.cluster.HierarchicalCluster",
      "flare.vis.data.Data"
    ]
  },
]
#render.js
/* eslint-disable no-param-reassign */
import * as d3 from 'd3';
import { data } from './renderUtils';

export default function chord(id) {
  const width = 1000;
  const height = 1000;
  const color = d3.scaleOrdinal(d3.schemeCategory10);
  const innerRadius = 250;

  const svg = d3
    .select(id)
    .append('svg')
    .attr('width', width)
    .attr('height', height)
    .attr('viewBox', [-width / 2, -height / 2, width, height]);
  // 弦布局
  const chordlayout = d3
    .chord()
    .padAngle(0.04)
    .sortSubgroups(d3.descending)
    .sortChords(d3.descending);
  const chords = chordlayout(data.matrix);
  // 布局转化数据
  const arc = d3
    .arc()
    .innerRadius(innerRadius)
    .outerRadius(innerRadius + 20);
  const ribbon = d3.ribbon().radius(innerRadius);

  const group = svg
    .append('g')
    .selectAll('g')
    .data(chords.groups)
    .join('g');

  group
    .append('path')
    .attr('fill', d => color(d.index))
    .attr('stroke', d => color(d.index))
    .attr('d', arc);

  group
    .append('text')
    .each(d => {
      d.angle = (d.startAngle + d.endAngle) / 2;
    })
    .attr('dy', '.35em')
    .attr(
      'transform',
      d => `
        rotate(${(d.angle * 180) / Math.PI - 90})
        translate(${innerRadius + 26})
        ${d.angle > Math.PI ? 'rotate(180)' : ''}
      `
    )
    .attr('text-anchor', d => (d.angle > Math.PI ? 'end' : null))
    .text(d => data.nameByIndex.get(d.index));

  // 添加一个提示框
  const tooltip = d3
    .select(id)
    .append('div')
    .attr('class', 'tooltip')
    .style('position', 'absolute')
    .style('background-color', '#fff')
    .style('opacity', 0);
  svg
    .append('g')
    .attr('fill-opacity', 0.67)
    .selectAll('path')
    .data(chords)
    .join('path')
    .attr('stroke', d => d3.rgb(color(d.source.index)).darker())
    .attr('fill', d => color(d.source.index))
    .attr('d', ribbon)
    .on('mouseover', function(d) {
      tooltip
        .html(data.nameByIndex.get(d.source.index))
        .style('left', `${d3.event.pageX}px`)
        .style('top', `${d3.event.pageY + 20}px`)
        .style('opacity', 1.0)
        .style('box-shadow', `10px 0px 0px${color(d.source.index)}`); // 在提示框后添加阴影
      d3.select(this).attr('fill', 'none');
    })
    .on('mouseout', function() {
      d3.select(this).attr('fill', a => color(a.source.index));
    });
}

#renderUtils.js

/* eslint-disable no-multi-assign */
const indexByName = new Map();
const nameByIndex = new Map();
let n = 0;
const matrix = [];
const imports = require('./data.json');

imports.forEach(d => {
  // eslint-disable-next-line no-param-reassign
  if (!indexByName.has((d = name(d.name)))) {
    nameByIndex.set(n, d);
    indexByName.set(d, (n += 1));
  }
});
imports.forEach(d => {
  const source = indexByName.get(name(d.name));
  let row = matrix[source];
  if (!row) row = matrix[source] = Array.from({ length: n }).fill(0);
  // eslint-disable-next-line no-plusplus
  d.imports.forEach(a => row[indexByName.get(name(a))]++);
});
matrix.splice(0, 1);
// Compute a unique index for each package name.
export const data = {
  matrix,
  indexByName,
  nameByIndex,
};

function name(string) {
  return string.substring(0, string.lastIndexOf('.')).substring(6);
}

你可能感兴趣的:(D3.js)