D3.js 如何在饼图周围添加带箭头的圆圈

问题描述 投票:0回答:1

我正在尝试用 d3 库表示饼图。 我尝试制作饼图,但在用箭头围绕饼图绘制线条时遇到问题。

  const data = [10, 10, 10, 10, 10, 10];
 import React, { useEffect, useRef } from "react";
import * as d3 from "d3";

const PieChart = ({ data }) => {
  const svgRef = useRef();

  useEffect(() => {
    createWheel();
  }, [data]);

  const createWheel = () => {
    const width = 200;
    const height = 200;
    const outerRadius = 100;
    const innerRadius = 0;
    const borderWidth = 1;
    const spaceBetweenSlices = 10;
    const arrowSize = 30;
    const padding = 20;

    const svg = d3
      .select(svgRef.current)
      .attr("width", width + padding * 2)
      .attr("height", height + padding * 2)
      .append("g")
      .attr(
        "transform",
        `translate(${width / 2 + padding},${height / 2 + padding})`
      );

    const pie = d3.pie();
    const pieData = pie(data);

    const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius);

    const arcs = svg
      .selectAll("arc")
      .data(pieData)
      .enter()
      .append("g")
      .attr("class", "arc");

    arcs
      .append("path")
      .attr("d", arc)
      .attr("fill", (d, i) => d3.schemeCategory10[i]);

    const borderArc = d3
      .arc()
      .innerRadius(outerRadius + spaceBetweenSlices)
      .outerRadius(outerRadius + borderWidth + spaceBetweenSlices);

    arcs
      .append("path")
      .attr("d", borderArc)
      .attr("fill", "none")
      .attr("stroke", (d, i) => d3.schemeCategory10[i])
      .attr("stroke-width", borderWidth);

    // Add greater-than signs at the end of each border
    arcs
      .append("text")
      .attr("x", (d) => borderArc.centroid(d)[0])
      .attr("y", (d) => borderArc.centroid(d)[1])
      .attr("dy", "0.35em")
      .attr("text-anchor", "middle")
      .attr("font-size", arrowSize)
      .attr("fill", (d, i) => d3.schemeCategory10[i])
      .text(">");
  };

  return <svg ref={svgRef}></svg>;
};

export default PieChart;


通过调整 SVG 容器内饼图的位置,向 SVG 元素添加填充。

在每个饼图边框的末尾,我们可以使用箭头符号吗? - 添加了示例设计图像

代码沙箱链接:https://codesandbox.io/s/condescending-banach-hzc6q8

javascript css reactjs d3.js react-hooks
1个回答
2
投票

最规范的做法 是使用

marker-end
。不过,对于您的用例,我认为在正确的位置绘制箭头并进行正确的旋转会更容易:

<!DOCTYPE html>

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.js"></script>
  </head>

  <body>
    <svg></svg>
    <script>
      const createWheel = () => {
        const width = 200;
        const height = 200;
        const outerRadius = 100;
        const innerRadius = 0;
        const borderWidth = 1;
        const spaceBetweenSlices = 10;
        const arrowSize = 30;
        const padding = 20;

        const svg = d3
          .select('svg')
          .attr('width', width + padding * 2)
          .attr('height', height + padding * 2)
          .append('g')
          .attr(
            'transform',
            `translate(${width / 2 + padding},${height / 2 + padding})`
          );

        const pie = d3.pie();
        const data = [Math.random() * 10, Math.random() * 10, Math.random() * 10, Math.random() * 10, Math.random() * 10, Math.random() * 10];
        const pieData = pie(data);

        const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius);

        const arcs = svg.selectAll('arc').data(pieData).enter()
          .append("g")
          .attr("class", "arc");

        arcs
          .append('path')
          .attr('d', arc)
          .attr('fill', (d, i) => d3.schemeCategory10[i]);

        const borderArc = d3
          .arc()
          .innerRadius(outerRadius + spaceBetweenSlices)
          .outerRadius(outerRadius + borderWidth + spaceBetweenSlices);

        arcs
          .append('path')
          .attr('d', borderArc)
          .attr('fill', 'none')
          .attr('stroke', (d, i) => d3.schemeCategory10[i])
          .attr('stroke-width', borderWidth);

        // Add greater-than signs at the end of each border
        arcs
          .append('path')
          .attr('d', 'M-10,-10 L0,0 L10,-10')
          .attr('transform', (d, i, j) => {
            let x = 
              (outerRadius + spaceBetweenSlices) *
              Math.cos(d.endAngle - Math.PI/2);
            let y = 
              (outerRadius + spaceBetweenSlices) *
              Math.sin(d.endAngle - Math.PI/2);
            return 'translate(' + x + ',' + y + '), rotate(' + ((d.endAngle * 180/Math.PI) - 90) + ')';
          })
          .attr('fill', 'none')
          .attr('stroke', (d, i) => d3.schemeCategory10[i])
          .attr('stroke-width', borderWidth * 2);
      };

    createWheel();
    </script>
  </body>
</html>

© www.soinside.com 2019 - 2024. All rights reserved.