You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在D3.js绘制的饼图外侧添加带箭头的线条

D3.js饼图外侧边框添加箭头解决方案

问题说明

使用D3.js结合React实现饼图可视化,饼图主体已绘制完成,但为外侧切片边框添加箭头符号时出现定位问题。当前用文本“>”替代箭头,无法精准对齐到边框末端,需要实现符合设计要求的箭头效果。

现有数据

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的<marker>元素实现箭头,它能自动绑定到路径末端,无需手动计算坐标,且样式统一可复用。

关键修改步骤

  1. 定义箭头标记:在SVG中添加<defs>块,里面定义箭头形状的<marker>,设置箭头的大小、朝向、颜色继承规则。
  2. 绑定箭头到边框路径:修改外侧边框的<path>元素,添加marker-end属性引用定义好的箭头标记。
  3. 移除原文本箭头:删除之前用文本“>”实现箭头的代码。

修改后的完整代码

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 = 8; // 调整箭头大小
    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})`
      );

    // 1. 定义箭头marker
    const defs = svg.append("defs");
    defs.append("marker")
      .attr("id", "sliceArrow")
      .attr("viewBox", "0 0 10 10")
      .attr("refX", 9) // 箭头锚点位置,对应路径末端
      .attr("refY", 5)
      .attr("markerWidth", arrowSize)
      .attr("markerHeight", arrowSize)
      .attr("orient", "auto") // 自动跟随路径方向
      .append("path")
      .attr("d", "M 0 0 L 10 5 L 0 10 Z") // 三角形箭头路径
      .attr("fill", "currentColor"); // 继承父元素stroke颜色

    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);

    // 2. 绑定箭头到边框路径
    arcs
      .append("path")
      .attr("d", borderArc)
      .attr("fill", "none")
      .attr("stroke", (d, i) => d3.schemeCategory10[i])
      .attr("stroke-width", borderWidth)
      .attr("marker-end", "url(#sliceArrow)"); // 引用箭头marker

    // 移除原文本箭头代码
  };

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

export default PieChart;

代码说明

  • <marker>的orient="auto"确保箭头自动匹配路径的末端方向,无需手动旋转。
  • fill="currentColor"让箭头颜色和边框的stroke颜色一致,保持样式统一。
  • 调整arrowSize可以控制箭头的大小,refX和refY设置箭头的锚点,确保箭头精准对齐到边框末端。

内容的提问来源于stack exchange,提问作者Pavan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 16:05:03