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

如何用D3.js实现指定的自定义环形分段形状?

如何用D3.js实现指定的自定义环形分段形状?

嘿,我懂你现在的困扰——手动凑数值调形状真的太折腾了,而且那个gapAngle确实没啥用,咱们来把代码捋顺,精准实现你要的图形。

首先聊聊原代码里的几个核心问题:

  • 手动硬编码角度偏移(比如1.517这种数值)完全不灵活,得靠精确计算来对齐内外段端点
  • gapAngle属于冗余变量,直接删掉就行,咱们让分段精准衔接
  • 连接线的倾斜逻辑应该和分段位置联动,而不是用固定值

下面是优化后的完整代码,我已经帮你移除了无用的gapAngle,用计算式精准对齐所有端点,连接线也能完美衔接内外分段:

document.addEventListener("DOMContentLoaded", () => {
    const width = 1000; // 画布宽度
    const height = 1000; // 画布高度
    const innerRingOuterRadius = 280; // 内部分段的外半径
    const outerRingInnerRadius = 295; // 外侧分段的内半径
    const segmentThickness = 3; // 统一所有分段的厚度
    const segmentColor = "#265152"; // 分段颜色
    const lineColor = "#FF0000"; // 连接线颜色
    const inclinationAngle = 0.06; // 连接线的倾斜角度(弧度)

    // 各分段的角度(角度制)
    const innerSegmentDegrees = [133, 133]; // 左右对称的两个内部分段
    const outerSegmentDegrees = [40, 40]; // 对应外侧的两个小分段

    // 转换为弧度
    const innerSegmentRadians = innerSegmentDegrees.map(deg => deg * Math.PI / 180);
    const outerSegmentRadians = outerSegmentDegrees.map(deg => deg * Math.PI / 180);

    // 计算内部分段的结束角度,用于定位外侧分段的起始位置
    const innerSegmentEnds = [
        innerSegmentRadians[0],
        Math.PI + innerSegmentRadians[1]
    ];

    // 创建SVG画布并居中
    const svg = d3
        .select("#c4")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", `translate(${width / 2}, ${height / 2})`);

    // 极坐标转笛卡尔坐标的工具函数
    const polarToCartesian = (radius, angle) => ({
        x: radius * Math.cos(angle),
        y: radius * Math.sin(angle),
    });

    // 绘制内部分段
    innerSegmentRadians.forEach((segmentAngle, index) => {
        const startAngle = index === 0 ? 0 : Math.PI; // 左右对称放置
        const endAngle = startAngle + segmentAngle;

        const arc = d3
            .arc()
            .innerRadius(innerRingOuterRadius - segmentThickness)
            .outerRadius(innerRingOuterRadius)
            .startAngle(startAngle)
            .endAngle(endAngle);

        svg.append("path")
            .attr("d", arc)
            .attr("fill", segmentColor);
    });

    // 绘制外侧分段(精准衔接内部分段的间隙)
    outerSegmentRadians.forEach((segmentAngle, index) => {
        const startAngle = innerSegmentEnds[index]; // 直接从内部分段结束处开始
        const endAngle = startAngle + segmentAngle;

        const arc = d3
            .arc()
            .innerRadius(outerRingInnerRadius)
            .outerRadius(outerRingInnerRadius + segmentThickness)
            .startAngle(startAngle)
            .endAngle(endAngle);

        svg.append("path")
            .attr("d", arc)
            .attr("fill", segmentColor);
    });

    // 绘制连接线(精准衔接内外分段的端点)
    const connectionPoints = [
        // 左侧内段结束点 -> 左侧外段起始点
        { innerAngle: innerSegmentEnds[0], outerAngle: innerSegmentEnds[0], direction: -1 },
        // 右侧内段结束点 -> 右侧外段起始点
        { innerAngle: innerSegmentEnds[1], outerAngle: innerSegmentEnds[1], direction: 1 },
        // 左侧外段结束点 -> 左侧内段对应点
        { innerAngle: innerSegmentEnds[0] - innerSegmentRadians[0] + outerSegmentRadians[0], outerAngle: innerSegmentEnds[0] + outerSegmentRadians[0], direction: -1 },
        // 右侧外段结束点 -> 右侧内段对应点
        { innerAngle: innerSegmentEnds[1] - innerSegmentRadians[1] + outerSegmentRadians[1], outerAngle: innerSegmentEnds[1] + outerSegmentRadians[1], direction: 1 }
    ];

    connectionPoints.forEach(({ innerAngle, outerAngle, direction }) => {
        const innerPoint = polarToCartesian(innerRingOuterRadius, innerAngle);
        // 应用倾斜角度,左右方向相反
        const inclinedOuterAngle = outerAngle + direction * inclinationAngle;
        const outerPoint = polarToCartesian(outerRingInnerRadius, inclinedOuterAngle);

        svg.append("line")
            .attr("x1", innerPoint.x)
            .attr("y1", innerPoint.y)
            .attr("x2", outerPoint.x)
            .attr("y2", outerPoint.y)
            .attr("stroke", lineColor)
            .attr("stroke-width", 2);
    });
});

几个关键优化说明:

  • 移除冗余变量:删掉了无用的gapAngle,直接通过内部分段的结束角度定位外侧分段的起始位置,确保分段间没有间隙
  • 替换硬编码偏移:所有端点位置都用计算式生成,不管你调整分段角度、半径还是厚度,图形都会自动对齐,不用再手动凑数字
  • 统一可配置项:新增segmentThickness变量,方便后续统一调整所有分段的厚度
  • 联动连接线逻辑:连接线的倾斜方向和分段位置绑定,确保左右两侧的线条对称且精准衔接

之后你要是想调整图形参数,直接修改顶部的变量值就行,不用再折腾内部的角度计算啦。

备注:内容来源于stack exchange,提问作者Global Ant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:14:30