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

如何实现带圆角分段的D3.js饼图?

D3.js饼图样式定制解决方案

需求概述

  • 饼图分段实现圆角效果
  • 分段间保留微小间隙
  • 标签置于圆角框内
  • 分段高度与数值关联

现有代码

import React from 'react';
import * as d3 from 'd3';
import './PieChart1.scss';

class PieChart extends React.Component {
    constructor(props) {
        super(props);
        this.myRef = React.createRef();
        this.state = {
            data: [],
            theme: this.props.theme ? this.props.theme : ['#bde0fe', '#2698f9', '#71bcfd', '#f1f8fe']
        };
    }

    componentDidMount() {
        var $this = this.myRef.current;

        d3.select($this)
            .selectAll('svg')
            .remove();

        const data = this.props.data;

        const width = parseInt(this.props.width, 10),
            height = parseInt(this.props.height, 10),
            radius = parseInt(this.props.r, 10),
            innerradius = parseInt(this.props.ir, 10);

        var color = d3.scaleOrdinal().range(this.state.theme);

        var arc = d3
            .arc()
            .outerRadius(radius)
            .innerRadius(innerradius);

        data.forEach(function(d) {
            d.total = +d.value;
        });

        var pie = d3
            .pie()
            .sort(null)
            .value(function(d) {
                return d.total;
            });

        var svg = d3
            .select($this)
            .append('svg')
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('class', 'piechart')
            .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

        var segments = svg.append('g').attr('class', 'segments');

        var slices = segments
            .selectAll('.arc')
            .data(pie(data))
            .enter()
            .append('g')
            .attr('class', 'arc');

        slices
            .append('path')
            .attr('d', arc)
            .attr('fill', function(d, i) {
                return color(i);
            })
            .transition()
            .attrTween('d', function(d) {
                var i = d3.interpolate(d.startAngle+0.1, d.endAngle);
                return function(t) {
                    d.endAngle = i(t);
                    return arc(d);
                }
            });

    }

    render() {
        return <div ref={this.myRef} className="PieChart" />;
    }
}
export default PieChart;

实现步骤

1. 圆角分段处理

D3原生d3.arc()不支持直接设置圆角,需自定义圆弧生成逻辑:

  • 定义固定圆角半径(如const cornerRadius = 6)
  • 重写路径生成逻辑,对每个分段的起始/结束角度的内外端点,添加圆弧片段替代原直线衔接
  • 针对角度差小于圆角对应角度的极小分段,单独处理避免路径异常

2. 分段间隙实现

通过d3.pie()的padAngle参数添加分段间微小间隙:

var pie = d3.pie()
  .sort(null)
  .value(d => d.total)
  .padAngle(0.02); // 控制间隙大小,值越大间隙越宽

3. 圆角标签框实现

  • 为每个分段创建包含文本和矩形的<g>容器
  • 绘制<rect>作为背景,通过rx、ry属性设置圆角
  • 先计算文本宽度,动态调整矩形宽高以包裹文本
  • 通过transform将标签定位到分段外侧合适位置(可结合arc.centroid()计算基准点)

4. 分段高度与数值关联

修改outerRadius为动态值,让分段高度随数值变化:

const maxValue = d3.max(data, d => d.value);
var arc = d3.arc()
  .innerRadius(innerradius)
  .outerRadius(d => innerradius + (d.data.value / maxValue) * (radius - innerradius));

参考方案整理

  1. 环形图圆角实现原理:通过三角函数计算圆角的圆心与半径,将分段端点的直线衔接替换为圆弧片段,同时处理相邻分段的衔接逻辑,避免圆角重叠。
  2. 通用圆角饼图方案:扩展d3.arc功能,新增cornerRadius配置项,自动为分段内外角添加圆角,同时兼容极小分段的特殊处理。
  3. 仅顶部圆角的实现:仅修改外侧弧的端点处理逻辑,保持内侧弧为直角,适配特定视觉风格的饼图需求。

内容的提问来源于stack exchange,提问作者The Old County

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 10:35:07