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

如何在环形图(Donut Chart)中实现扇区均匀分隔?

环形图扇区无法均匀分隔的修复方案

问题分析

你的代码存在几个核心问题导致扇区无法均匀分隔:

  • 路径绘制时未使用扣除间距后的实际角度,仍依赖初始计算的item.value判断大弧标志,逻辑错位
  • 用于创建间距的角度调整仅作用于标签位置,未应用到扇区路径的角度计算
  • 累计角度的累加逻辑未完全匹配扇区+间距的分配规则

修复步骤

  • 移除冗余的calculatedSlices和total计算,直接基于实际扇区角度处理逻辑
  • 将间距调整整合到扇区的起始/结束角度计算中,确保路径绘制使用正确范围
  • 修正大弧标志的判断逻辑,基于实际扇区角度是否超过180度来判定
  • 调整累计角度的累加方式,保证扇区与间距的角度分配准确

修改后的完整代码

import { storyblokEditable } from "@storyblok/react";
import { type BlokType } from "../../../types/storyblok";

interface AnimatedPieBlokProps {
  blok: BlokType;
}

function AnimatedPieBlok({ blok }: AnimatedPieBlokProps) {
  const { pieSlices } = blok;
  const totalSlices = pieSlices.length;
  const spacing = 1; // 扇区间的间距角度
  const sliceAngle = (360 - spacing * totalSlices) / totalSlices; // 每个扇区的实际角度(扣除总间距)
  let cumulativeAngle = 0; // 累计角度,初始为0

  return (
    <div {...storyblokEditable(blok)} key={blok._uid} className="flex flex-row justify-center">
      <svg width="450" height="450" viewBox="0 0 200 200" className="flex-1">
        {pieSlices.map((item: BlokType, index: number) => {
          const words = item.title.split(' ');
          const firstWord = words[0];
          const secondWord = words[1];

          // 计算当前扇区的起始/结束角度(转换为弧度)
          const startAngle = cumulativeAngle * (Math.PI / 180);
          // 结束角度 = 起始角度 + 扇区实际角度
          const endAngle = (cumulativeAngle + sliceAngle) * (Math.PI / 180);

          // 计算路径端点坐标
          const x1 = 100 + 100 * Math.cos(startAngle);
          const y1 = 100 + 100 * Math.sin(startAngle);
          const x2 = 100 + 100 * Math.cos(endAngle);
          const y2 = 100 + 100 * Math.sin(endAngle);

          // 计算标签和图片的中点角度(基于实际扇区角度)
          const midAngle = (startAngle + endAngle) / 2;
          const labelX = 100 + 65 * Math.cos(midAngle);
          const labelY = 100 + 65 * Math.sin(midAngle);
          const imageX = 100 + 40 * Math.cos(midAngle);
          const imageY = 100 + 40 * Math.sin(midAngle);

          // 累计角度 += 扇区角度 + 间距角度
          cumulativeAngle += sliceAngle + spacing;

          // 判断是否需要使用大弧标志(角度>180度时为1)
          const isLargeArc = sliceAngle > 180 ? 1 : 0;

          return (
            <g
              key={index}
              className="transition-transform duration-300 ease-in-out hover:scale-105 relative hover:z-10"
            >
              <path
                d={`M 100,100 L ${x1},${y1} A 100,100 0 ${isLargeArc} 1 ${x2},${y2} Z`}
                fill={item.backgroundColor}
              />
              <image
                href={item.image.filename}
                x={imageX - 15}
                y={imageY - 15}
                width="30"
                height="30"
              />
              <text
                x={labelX}
                y={labelY}
                fill="white"
                fontSize="7px"
                textAnchor="middle"
                alignmentBaseline="middle"
              >
                <tspan>{firstWord}</tspan>
                {secondWord ? <tspan x={labelX} dy="1.2em">{secondWord}</tspan> : null}
              </text>
            </g>
          );
        })}
        <circle cx="100" cy="100" r="30" fill="white" />
        <circle cx="100" cy="100" r="29" fill="none" stroke="#140965" strokeWidth="3" />
      </svg>
    </div>
  );
}

export default AnimatedPieBlok;

关键说明

  • 所有角度计算直接基于sliceAngle(扣除总间距后的均匀角度),确保每个扇区大小一致
  • 路径的大弧标志由实际扇区角度决定,避免逻辑错误
  • 累计角度严格按照「扇区角度+间距角度」累加,保证扇区和间距的均匀分布
  • 移除了冗余的calculatedSlices映射,简化代码逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:16:17