Pavan
Pavan

Reputation: 25

D3.js how add circle with arrows around pie chart

I am trying to represent a pie chart with d3 library. I tried to make pie chart but I have problem in making lines around pie chart with arrows.

  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;


Added padding to the SVG element by adjusting the position of the pie chart within the SVG container.

At the end of each pie slice border, can we arrow symbol? - sample design image added

Code-sandbox Link: https://codesandbox.io/s/condescending-banach-hzc6q8

Upvotes: 1

Views: 160

Answers (1)

Mark
Mark

Reputation: 108567

The canoical way to do would be to use marker-end. For your use case, though, I think it would be easier just to draw the arrow heads at the correct positions with correct rotation:

<!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>

Upvotes: 2

Related Questions