antvis/G2

V5能不能实现V4的饼图-半径大小 #6563

Leezen-Li posted onGitHub

问题描述

在官网找了很久,V5没有找到我要的百分比占比以及半径跟数据相关的饼状图或者玫瑰图,但是在V4找到了,但是V5迁移后写法也有点变化,写了一下没有写出来

重现链接

No response

重现步骤

// 计算最大值 const max = useMemo(() => { return Math.max(...realData.map((obj) => obj[yLabel] ?? 0), 0); }, [realData, yLabel]);

// 定义自定义图形组件 function SliceShape(style, context) { const { document } = context; return (P, value, defaults) => { const [p0, p1, p2, p3] = P;

  // 计算中点 pm
  // 计算比例,确定图形的宽度
  const percent = value.items[1].value / max;
  const xWidth = p2[0] - p1[0]; // X 轴宽度
  const width = xWidth * percent; // 根据百分比计算宽度

  // 计算路径点,确保每个图形的路径正确
  const points = [
    [p0[0], p0[1]], // 第一个点
    [p1[0], p1[1]], // 第二个点
    [p0[0] + width, p2[1]], // 根据宽度计算的第三个点
    [p0[0] + width, p3[1]], // 根据宽度计算的第四个点
  ];

  // 创建路径
  const shape = document.createElement("path", {
    style: {
      ...style,
      // fill: value.color, // 使用传入的颜色
    },
    attrs: {
      path: [
        ["M", points[0][0], points[0][1]], // 移动到第一个点
        ["L", points[1][0], points[1][1]], // 画到第二个点
        ["L", points[2][0], points[2][1]], // 画到第三个点
        ["L", points[3][0], points[3][1]], // 画到第四个点
        "Z", // 闭合路径
      ],
    },
  });

  return shape;
};

预期行为

No response

平台

  • 操作系统: [macOS, Windows, Linux, React Native ...]
  • 网页浏览器: [Google Chrome, Safari, Firefox]

屏幕截图或视频(可选)

No response

补充说明(可选)

No response


posted by Leezen-Li 4 months ago

玫瑰图的话可以看看v5这个例子:https://g2.antv.antgroup.com/zh/examples/general/rose/#rose

posted by interstellarmt 4 months ago

玫瑰图的话可以看看v5这个例子:https://g2.antv.antgroup.com/zh/examples/general/rose/#rose

哦哦,可能我描述的不太对,我是希望不仅是半径,还有弧度百分比占比,极坐标系玫瑰图都是半径跟数据有关,但是弧度是等分的 我是希望像下图一样的效果 <img width="1144" alt="image" src="https://github.com/user-attachments/assets/a2499545-af2f-41a9-8781-1ab7bb24205c" />

posted by Leezen-Li 4 months ago

你可这样写试一试

import * as G2 from '@antv/g2';

export function issue6563(context) {
  const { container, canvas } = context;
  const data = [
    { type: '分类一', value: 27 },
    { type: '分类二', value: 25 },
    { type: '分类三', value: 18 },
    { type: '分类四', value: 15 },
    { type: '分类五', value: 10 },
    { type: 'Other', value: 5 },
  ];

  const max = Math.max(...data.map((obj) => obj.value ?? 0), 0);
  function ShapeSlice(style, context) {
    const { document } = context;
    return (P, value, defaults) => {
      const { color: defaultColor } = defaults;
      const [p0, p1, p2, p3] = P;
      const pm = [(p0[0] + p1[0]) / 2, p0[1]];
      const { color = defaultColor } = value;

      const percentValue = value.items[0].value;
      const percent =
        typeof percentValue === 'string'
          ? Number.parseFloat(percentValue.replace('%', '')) / max
          : Number(percentValue) / max;

      const transformPoints = scalePoints(p1, p0, p3, percent);

      const svg = pathArrayToString(createArcPath(...transformPoints));

      return document.createElement('path', {
        style: {
          fill: color,
          d: svg,
          ...style,
        },
      });
    };
  }

  G2.register('shape.interval.slice', ShapeSlice);

  const chart = new G2.Chart({
    container: container,
    autoFit: true,
    canvas,
  });

  chart
    .coordinate({ type: 'theta', outerRadius: 0.8 })
    .interval()
    .data(data)
    .transform({ type: 'stackY' })
    .encode('y', 'value')
    .encode('color', 'type')
    .legend('color', {
      position: 'bottom',
      layout: { justifyContent: 'center' },
    })
    .label({
      position: 'outside',
      text: (data) => ` ${data.value}%`,
    })
    .tooltip((data) => ({
      name: data.type,
      value: `${data.value}%`,
    }))
    .encode('shape', 'slice');

  const finished = chart.render();

  return {
    chart,
    finished,
  };
}

function scalePoints(
  center: [number, number],
  p1: [number, number],
  p2: [number, number],
  factor: number,
): [[number, number], [number, number], [number, number]] {
  const dx1 = p1[0] - center[0];
  const dy1 = p1[1] - center[1];

  const dx2 = p2[0] - center[0];
  const dy2 = p2[1] - center[1];

  const scaledP1: [number, number] = [
    center[0] + dx1 * factor,
    center[1] + dy1 * factor,
  ];
  const scaledP2: [number, number] = [
    center[0] + dx2 * factor,
    center[1] + dy2 * factor,
  ];

  return [center, scaledP1, scaledP2];
}

function pathArrayToString(pathArray) {
  return pathArray
    .map((item) => {
      if (Array.isArray(item)) {
        return item.join(' '); // 将数组元素连接成字符串
      }
      return item; // 对于 "Z" 直接返回
    })
    .join(' '); // 用空格连接整个路径
}
function createArcPath(center: number[], point1: number[], point2: number[]) {
  // 计算两个点到中心点的距离(半径)
  const radius1 = Math.sqrt(
    Math.pow(point1[0] - center[0], 2) + Math.pow(point1[1] - center[1], 2),
  );
  const radius2 = Math.sqrt(
    Math.pow(point2[0] - center[0], 2) + Math.pow(point2[1] - center[1], 2),
  );

  // 确保半径相等,以便形成一个闭合的弧
  const radius = Math.min(radius1, radius2);

  // 计算角度(单位:弧度)
  const angle1 = Math.atan2(point1[1] - center[1], point1[0] - center[0]);
  const angle2 = Math.atan2(point2[1] - center[1], point2[0] - center[0]);

  // 计算弧的路径(从angle1到angle2)
  const largeArcFlag = angle2 - angle1 > Math.PI ? 1 : 0;

  // 生成路径
  return [
    ['M', center[0], center[1]], // 移动到圆心
    ['L', point1[0], point1[1]], // 画一条直线到第一个点
    ['A', radius, radius, 0, 0, 0, point2[0], point2[1]], // 绘制弧
    'Z', // 闭合路径
  ];
}
posted by BQXBQX 4 months ago

但是我又遇到了一个新问题,首次渲染的时候渲染错误,autofit 后渲染正确了🤔

https://github.com/user-attachments/assets/998b62b1-797e-4dda-8429-c1f7cfda8182

posted by BQXBQX 4 months ago

但是我又遇到了一个新问题,首次渲染的时候渲染错误,自动调整后渲染正确了🤔

钉钉录音_2024-12-25.012647.mp4

感谢,我复现到了,确实第一次展示位置会不对,同时点击图例之后,消失的图例再次出现也会错位显示,我这边也在看看怎么处理,非常感谢🙏

posted by Leezen-Li 4 months ago

<img width="1278" alt="image" src="https://github.com/user-attachments/assets/b25dfe9f-0fbd-4cdb-befa-9d5f8db54291" /> 问题在于默认动画对 path 进行了复写,我觉得这是这是一个 bug。

@Leezen-Li 你可以先添加 .animate(false) 禁用动画,禁用动画后,渲染 bug 不会出现

posted by BQXBQX 4 months ago

<img alt="image" width="1278" src="https://private-user-images.githubusercontent.com/132878537/398583488-b25dfe9f-0fbd-4cdb-befa-9d5f8db54291.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzUxNzczMDQsIm5iZiI6MTczNTE3NzAwNCwicGF0aCI6Ii8xMzI4Nzg1MzcvMzk4NTgzNDg4LWIyNWRmZTlmLTBmYmQtNGNkYi1iZWZhLTlkNWY4ZGI1NDI5MS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQxMjI2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MTIyNlQwMTM2NDRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1lZWQyZWY3YTAyZDA2MTg4MzNiNTNiMDlkZDYwNTRkNjE4NmE3NGNjNDM2YWNiNzViM2E1ODZiNGRmMmUxYWZhJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.7zLaJdEpI92nSi8q1_cqkKf7d4PByGOKeV2QTdfrCKM"> 问题在于默认动画对 path 进行了复写,我觉得这是这是一个 bug。 @Leezen-Li 你可以先添加 .animate(false) 禁用动画,禁用动画后,渲染 bug 不会出现

好的,已经成功实现,感谢

posted by Leezen-Li 4 months ago

Fund this Issue

$0.00
Funded

Pull requests