D3.js v5.0 散点图

数据结构:
[
{
“name”: ‘’,
“x”: ‘’,
“y”:’’
},
]

import * as d3 from 'd3';

export default function scatter(id, data) {
  const width = 600;
  const height = 400;
  const margin = { top: 20, right: 30, bottom: 30, left: 60 };
  const svg = d3
    .select(id)
    .append('svg')
    .attr('class', 'mainBubbleSVG')
    .attr('width', width)
    .attr('height', height);
    //比例尺
  const x = d3
    .scaleLinear()
    .domain(d3.extent(data, d => d.x))
    .nice()
    .range([margin.left, width - margin.right]);
  const y = d3
    .scaleLinear()
    .domain(d3.extent(data, d => d.y))
    .nice()
    .range([height - margin.bottom, margin.top]);
    //坐标轴
  const xAxis = g =>
    g
      .attr('transform', `translate(0,${height - margin.bottom})`)
      .call(d3.axisBottom(x))
      .call(() => g.select('.domain').remove())
      .call(() =>
        g
          .append('text')
          .attr('x', width - margin.right)
          .attr('y', -4)
          .attr('fill', '#000')
          .attr('font-weight', 'bold')
          .attr('text-anchor', 'end')
          .text(data.x)
      );
  const yAxis = g =>
    g
      .attr('transform', `translate(${margin.left},0)`)
      .call(d3.axisLeft(y))
      .call(() => g.select('.domain').remove())
      .call(() =>
        g
          .select('.tick:last-of-type text')
          .clone()
          .attr('x', 4)
          .attr('text-anchor', 'start')
          .attr('font-weight', 'bold')
          .text(data.y)
      );
      //添加坐标轴
  svg.append('g').call(xAxis);

  svg.append('g').call(yAxis);
  //打点
  svg
    .append('g')
    .attr('stroke', 'steelblue')
    .attr('stroke-width', 1.5)
    .attr('fill', 'none')
    .selectAll('circle')
    .data(data)
    .join('circle')
    .attr('cx', d => x(d.x))
    .attr('cy', d => y(d.y))
    .attr('r', 2);
    //标注文字
  svg
    .append('g')
    .attr('font-family', 'sans-serif')
    .attr('font-size', 10)
    .selectAll('text')
    .data(data)
    .join('text')
    .attr('x', d => x(d.x))
    .attr('y', d => y(d.y))
    .text(d => d.name)
    .call(dodge);
  function dodge(text, iterations = 300) {
    const nodes = text.nodes();
    const left = () => text.attr('text-anchor', 'start').attr('dy', '0.32em');
    const right = () => text.attr('text-anchor', 'end').attr('dy', '0.32em');
    const top = () => text.attr('text-anchor', 'middle').attr('dy', '0.0em');
    const bottom = () => text.attr('text-anchor', 'middle').attr('dy', '0.8em');
    const points = nodes.map(node => ({
      fx: +node.getAttribute('x'),
      fy: +node.getAttribute('y'),
    }));
    const labels = points.map(({ fx, fy }) => ({ x: fx, y: fy }));
    const links = points.map((source, i) => ({ source, target: labels[i] }));

    const simulation = d3
      .forceSimulation(points.concat(labels))
      .force('charge', d3.forceManyBody().distanceMax(80))
      .force(
        'link',
        d3
          .forceLink(links)
          .distance(4)
          .iterations(4)
      )
      .stop();

    for (let i = 0; i < iterations; i+=1) simulation.tick();

    text
      .attr('x', (_, i) => labels[i].x)
      .attr('y', (_, i) => labels[i].y)
      .each(function(_, i) {
        const a = Math.atan2(labels[i].y - points[i].fy, labels[i].x - points[i].fx);
        d3.select(this).call(
          a > Math.PI / 4 && a <= (Math.PI * 3) / 4
            ? bottom
            : a > -Math.PI / 4 && a <= Math.PI / 4
            ? left
            : a > (-Math.PI * 3) / 4 && a <= (Math.PI * 3) / 4
            ? top
            : right
        );
      });
  }
}

D3.js v5.0 散点图_第1张图片

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