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

如何使用react-native-svg实现饼图路径圆角效果?

问题描述

我想让饼图的边角呈现类似borderRadius的圆润效果,但不清楚pathData里缺少哪些元素。尝试使用strokeLineJoin和strokeLineCap完全没用,更新pathData中的弧数据也没有效果。我不确定是否是饼图切片间的间距限制了圆角的显示,但即使设置极高的圆角值也没有变化。

实现代码如下:

import React from 'react';
import { View } from 'react-native';
import Svg, { G, Path, Circle } from 'react-native-svg';

interface PieSlice {
  value: number;
  color: string;
}

interface CustomPieChartProps {
  data: PieSlice[];
  size: number;
}

const CustomPieChart: React.FC<CustomPieChartProps> = ({ data, size }) => {
  const total = data.reduce((sum, slice) => sum + slice.value, 0);
  let cumulativeValue = 0;
  const cornerRadius = size / 20; // Adjust this value for more or less curvature

  const slices = data.map((slice) => {
    const startAngle = (cumulativeValue / total) * 2 * Math.PI;
    cumulativeValue += slice.value;
    const endAngle = (cumulativeValue / total) * 2 * Math.PI;

    const largeArcFlag = endAngle - startAngle > Math.PI ? 1 : 0;

    // Calculate points with corner radius
    const x1 = size / 2 + (size / 2) * Math.cos(startAngle);
    const y1 = size / 2 + (size / 2) * Math.sin(startAngle);

    const x2 = size / 2 + (size / 2) * Math.cos(endAngle);
    const y2 = size / 2 + (size / 2) * Math.sin(endAngle);

    const pathData = `
      M${size / 2},${size / 2}
      L${x1},${y1}
      A${size / 2},${size / 2} 0 ${largeArcFlag} 1 ${x2},${y2}
      L${size / 2},${size / 2}
      Z
    `;

    return (
      <Path
        key={`${slice.color}-${slice.value}`}
        d={pathData}
        fill={slice.color}
        stroke="white"
        strokeWidth={cornerRadius}
        strokeLinejoin="round"
        strokeLinecap="round"
      />
    );
  });

  return (
    <View style={{ justifyContent: 'center', alignItems: 'center' }}>
      <Svg width={size} height={size}>
        <G>
          {slices}
          <Circle cx={size / 2} cy={size / 2} r={size / 4} fill="white" />
        </G>
      </Svg>
    </View>
  );
};

export default CustomPieChart;

当前效果截图:
当前饼图效果

解决方案

问题根源

你现在的写法是用stroke实现切片间距,同时试图依赖strokeLineJoin做圆角,但strokeLineJoin只作用于路径线段的连接处,而饼图切片的角在中心和弧的两端,且白色stroke会覆盖圆角效果,所以这种方式根本行不通。正确的做法是直接修改pathData,给切片的外弧两端添加原生圆角弧。

修改后的代码

import React from 'react';
import { View } from 'react-native';
import Svg, { G, Path, Circle } from 'react-native-svg';

interface PieSlice {
  value: number;
  color: string;
}

interface CustomPieChartProps {
  data: PieSlice[];
  size: number;
}

const CustomPieChart: React.FC<CustomPieChartProps> = ({ data, size }) => {
  const total = data.reduce((sum, slice) => sum + slice.value, 0);
  let cumulativeValue = 0;
  const cornerRadius = size / 20; // 圆角大小,可自行调整
  const outerRadius = size / 2;

  const slices = data.map((slice) => {
    const startAngle = (cumulativeValue / total) * 2 * Math.PI;
    cumulativeValue += slice.value;
    const endAngle = (cumulativeValue / total) * 2 * Math.PI;
    const sliceAngle = endAngle - startAngle;

    const largeArcFlag = sliceAngle > Math.PI ? 1 : 0;
    // 计算圆角对应的角度偏移,给圆角留出空间
    const angleOffset = Math.asin(cornerRadius / outerRadius);
    // 调整大弧的起始/结束角度,避开圆角区域
    const adjustedStartAngle = startAngle + angleOffset;
    const adjustedEndAngle = endAngle - angleOffset;

    // 切片原始端点(圆角的起点/终点)
    const startX = size / 2 + outerRadius * Math.cos(startAngle);
    const startY = size / 2 + outerRadius * Math.sin(startAngle);
    const endX = size / 2 + outerRadius * Math.cos(endAngle);
    const endY = size / 2 + outerRadius * Math.sin(endAngle);

    // 调整后大弧的端点
    const arcStartX = size / 2 + outerRadius * Math.cos(adjustedStartAngle);
    const arcStartY = size / 2 + outerRadius * Math.sin(adjustedStartAngle);
    const arcEndX = size / 2 + outerRadius * Math.cos(adjustedEndAngle);
    const arcEndY = size / 2 + outerRadius * Math.sin(adjustedEndAngle);

    // 圆角弧的绘制方向(顺时针)
    const roundArcDir = 1;

    // 构造带圆角的路径:中心→原始起点→圆角弧→大弧→圆角弧→原始终点→中心
    const pathData = `
      M${size / 2},${size / 2}
      L${startX},${startY}
      A${cornerRadius},${cornerRadius} 0 0 ${roundArcDir} ${arcStartX},${arcStartY}
      A${outerRadius},${outerRadius} 0 ${largeArcFlag} 1 ${arcEndX},${arcEndY}
      A${cornerRadius},${cornerRadius} 0 0 ${roundArcDir} ${endX},${endY}
      L${size / 2},${size / 2}
      Z
    `.replace(/\s+/g, ' ').trim();

    return (
      <Path
        key={`${slice.color}-${slice.value}`}
        d={pathData}
        fill={slice.color}
        stroke="white"
        strokeWidth={2} // 切片间距,可自行调整
      />
    );
  });

  return (
    <View style={{ justifyContent: 'center', alignItems: 'center' }}>
      <Svg width={size} height={size}>
        <G>
          {slices}
          <Circle cx={size / 2} cy={size / 2} r={size / 4} fill="white" />
        </G>
      </Svg>
    </View>
  );
};

export default CustomPieChart;

关键修改说明

  1. 角度偏移计算:通过Math.asin(cornerRadius / outerRadius)算出圆角对应的角度偏移,调整大弧的起始/结束位置,给圆角留出绘制空间。
  2. 路径重构:把路径改成「中心→切片原始端点→小圆角弧→大弧→小圆角弧→切片另一端点→中心」的结构,让圆角成为路径的一部分,不再依赖stroke属性。
  3. 间距调整:将strokeWidth改为固定小数值,避免过大的stroke覆盖圆角效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 08:20:08