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

如何用D3.js实现仅顶部带圆角的环形图?

实现仅外侧分段带圆角的D3.js环形图

要实现仅外侧边角带圆角的环形图,核心是绕过D3默认cornerRadius全局生效的限制,手动构建扇形路径,只在外侧的两个角添加圆角,内侧保持直角。下面是具体实现方案:

核心实现逻辑

  • 用D3的pie()生成器处理数据,得到每个扇形的起始/结束角度
  • 自定义路径生成函数,拆分扇形为「外侧圆角弧段」+「内侧直角弧段」+「连接直线」三部分,手动拼接成完整路径
  • 处理小角度扇形的边界问题,避免圆角重叠导致的路径异常

完整代码示例

// 基础配置
const width = 400;
const height = 400;
const innerRadius = 100;
const outerRadius = 180;
const cornerRadius = 12;
const data = [30, 50, 20, 40];
const color = d3.scaleOrdinal(d3.schemeCategory10);

// 创建SVG容器
const svg = d3.select('body')
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .append('g')
  .attr('transform', `translate(${width/2}, ${height/2})`);

// 生成饼图数据(包含startAngle/endAngle)
const pieGenerator = d3.pie();
const arcs = pieGenerator(data);

// 自定义带外侧圆角的弧生成函数
function customArc(d) {
  const start = d.startAngle;
  const end = d.endAngle;
  const angleDiff = end - start;
  const halfPi = Math.PI / 2;
  // 最小角度阈值:避免圆角重叠
  const minAngle = 2 * cornerRadius / outerRadius;

  // 角度过小时,回退到普通环形图
  if (angleDiff < minAngle) {
    return d3.arc()
      .innerRadius(innerRadius)
      .outerRadius(outerRadius)
      (d);
  }

  // 计算外侧圆角的起始/结束锚点
  const outerAnchorStartX = Math.cos(start - halfPi) * (outerRadius - cornerRadius);
  const outerAnchorStartY = Math.sin(start - halfPi) * (outerRadius - cornerRadius);
  const outerAnchorEndX = Math.cos(end - halfPi) * (outerRadius - cornerRadius);
  const outerAnchorEndY = Math.sin(end - halfPi) * (outerRadius - cornerRadius);

  // 外侧圆角的圆弧段
  const outerCornerStart = `A ${cornerRadius} ${cornerRadius} 0 0 1 ${Math.cos(start - halfPi) * outerRadius} ${Math.sin(start - halfPi) * outerRadius}`;
  const outerCornerEnd = `A ${cornerRadius} ${cornerRadius} 0 0 1 ${outerAnchorEndX} ${outerAnchorEndY}`;

  // 外侧中间弧段(跳过圆角区域)
  const outerMiddleArc = d3.arc()
    .innerRadius(outerRadius)
    .outerRadius(outerRadius)
    .startAngle(start + cornerRadius / outerRadius)
    .endAngle(end - cornerRadius / outerRadius)
    ({})
    .replace(/^M[^ ]+ [^ ]+ /, '') // 移除默认起始点
    .replace(/ Z$/, ''); // 移除闭合命令

  // 内侧直角弧段
  const innerArc = d3.arc()
    .innerRadius(innerRadius)
    .outerRadius(innerRadius)
    .startAngle(end)
    .endAngle(start)
    ({})
    .replace(/^M[^ ]+ [^ ]+ /, '')
    .replace(/ Z$/, '');

  // 拼接完整路径
  return `M ${outerAnchorStartX} ${outerAnchorStartY} 
          ${outerCornerStart} 
          ${outerMiddleArc} 
          ${outerCornerEnd} 
          L ${Math.cos(end - halfPi) * innerRadius} ${Math.sin(end - halfPi) * innerRadius} 
          ${innerArc} 
          L ${outerAnchorStartX} ${outerAnchorStartY} 
          Z`;
}

// 绘制环形图
svg.selectAll('.arc')
  .data(arcs)
  .enter()
  .append('path')
  .attr('class', 'arc')
  .attr('d', customArc)
  .attr('fill', (d, i) => color(i));

关键细节说明

  • 手动拆分路径:将扇形拆分为外侧圆角锚点、圆角弧、中间弧、内侧弧,确保只有外侧两个角有圆角
  • 角度阈值判断:当扇形角度小于圆角对应的弧度时,自动回退到普通环形图,避免路径渲染异常
  • 坐标转换:D3的角度基准是y轴正方向,通过减去Math.PI/2转换为标准笛卡尔坐标计算

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 02:23:13