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

如何为Mapbox donutSegment聚合SVG添加指定宽度的白色描边

问题原因

你出现偏移的核心原因是 donutSegment 函数内所有路径坐标都是以 (r, r) 为圆心计算的,但你为了加描边扩容SVG画布后,新的画布圆心变成了 (r + STROKE_WIDTH, r + STROKE_WIDTH),二者坐标基准不匹配,导致环形图整体往左上偏移了等于STROKE_WIDTH的距离。

最简修复方案

不需要修改donutSegment的原有逻辑,只要把所有环形片段用<g>标签包裹,添加对应偏移即可,改动量最小:

export function createDonutChart(props: Record<string, number>) {
  // code for creating an SVG donut chart from feature properties
  const offsets: number[] = [];
  const counts = Object.entries(props);

  let total = 0;
  for (const [, count] of counts) {
    offsets.push(total);
    if (count) {
      total += count;
    }
  }
  const fontSize = 16;
  const r = 18;
  const stroke_r = r + STROKE_WIDTH;
  const r0 = Math.round(r * 0.6);
  const w = stroke_r * 2;

  return (
    <div>
      <svg
        width={w}
        height={w}
        viewBox={`0 0 ${w} ${w} `}
        textAnchor="middle"
        style={{ font: `${fontSize}px sans-serif`, display: 'block' }}
      >
        <circle cx={stroke_r} cy={stroke_r} r={stroke_r} fill="white" />
        {/* 新增g标签,把环形整体偏移STROKE_WIDTH距离,对齐新画布圆心 */}
        <g transform={`translate(${STROKE_WIDTH}, ${STROKE_WIDTH})`}>
          {counts.map(([color, count], i) =>
            donutSegment(offsets[i] / total, (offsets[i] + (count ?? 0)) / total, r, r0, color),
          )}
        </g>
        <text dominantBaseline="central" transform={`translate(${stroke_r}, ${stroke_r})`}>
          {total.toLocaleString()}
        </text>
      </svg>
    </div>
  );
}

可选更精简的描边方案

如果你不想额外加底层白色圆形,也可以直接通过描边属性实现效果,不需要调整环形偏移:
把底层的白色圆形替换为:

<circle cx={r} cy={r} r={r} fill="none" stroke="white" stroke-width={STROKE_WIDTH * 2} />

同时把SVG画布宽高、viewBox改回r*2即可。原理是SVG的描边会往路径两侧延伸,设置2倍STROKE_WIDTH的描边后,内侧的描边会被上层的环形图案覆盖,只会露出外侧等于STROKE_WIDTH的白色描边。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 17:06:06