import { Chart } from '@antv/g2';

const data = [ { item: '事例一', count: 40, percent: 0.4 }, { item: '事例二', count: 21, percent: 0.21 }, { item: '事例三', count: 17, percent: 0.17 }, { item: '事例四', count: 13, percent: 0.13 }, { item: '事例五', count: 9, percent: 0.09 }, ];

const chart = new Chart({ container: 'container', autoFit: true, height: 500, }); // 新建一个 view 用来单独渲染Annotation const innerView = chart.createView(); chart.coordinate('theta', { radius: 0.75, innerRadius: 0.5, });

chart.data(data);

chart.scale('percent', { formatter: val => { val = val * 100 + '%'; return val; }, });

chart.tooltip(false);

// 声明需要进行自定义图例字段: 'item' chart.legend('item', { position: 'right', // 配置图例显示位置 custom: true, // 关键字段,告诉 G2,要使用自定义的图例 items: data.map((obj, index) => { return { name: obj.item, // 对应 itemName value: obj.percent, // 对应 itemValue marker: { symbol: 'square', // marker 的形状 style: { r: 5, // marker 图形半径 fill: chart.getTheme().colors10[index], // marker 颜色,使用默认颜色,同图形对应 }, }, }; }), itemValue: { style: { fill: '#999', }, formatter: val => ${val * 100}% }, });

chart .interval() .adjust('stack') .position('percent') .color('item') .style({ fillOpacity: 1, }) .state({ active: { style: element => { const shape = element.shape; return { lineWidth: 10, stroke: shape.attr('fill'), strokeOpacity: shape.attr('fillOpacity'), }; }, }, });

// 移除图例点击过滤交互 chart.removeInteraction('legend-filter'); chart.interaction('element-active');

let firstPoint1; let firstPoint2; chart.on('afterrender', () => { // label 绘制图层 let labelGroup = chart.foregroundGroup.findById('customLabels'); if (labelGroup) { labelGroup.clear(); } else { labelGroup = chart.foregroundGroup.addGroup({ capture: false, id: 'customLabels' }); } const offset = 30; // 拐点折线的长度 const textOffset = 8; const elements = chart.geometries[0].elements; const coordinate = chart.getCoordinateByIndex(0); const center = coordinate.getCenter(); const radius = coordinate.getRadius();

const count = elements.length; let preWidth = 0;

for (let i = 0; i < count; i++) { const label = labelGroup.addGroup(); const element = elements[i]; const originData = element.data; const mappingData = element.getModel(); if (i === count - 1) { // 最后一个图形 label 横着长 label.addShape('path', { attrs: { path: [ ['M', center.x, center.y], ['L', center.x + radius + offset, center.y], ], stroke: mappingData.color, lineWidth: 1, }, }); label.addShape('text', { attrs: { x: center.x + radius + offset + textOffset, y: center.y, text: originData.count + originData.count, textBaseline: 'middle', fill: '#000', }, }); } else { const nextElement = elements[i + 1]; const nextBBox = nextElement.getBBox(); const bbox = element.getBBox(); // 第一个点 const width = bbox.maxX - nextBBox.maxX; const pointRadius = radius - preWidth - (width / 2); const point1 = Util.polarToCartesian(center.x, center.y, pointRadius, - 3 * Math.PI / 8 + (Math.PI / 8) * i); let point2; if (i === 0) { point2 = { x: bbox.maxX, y: bbox.minY }; firstPoint2 = point2; firstPoint1 = point1; } else { point2 = { x: Math.min(firstPoint2.x + point1.x - firstPoint1.x, elements[0].getBBox().maxX), y: firstPoint2.y + point1.y - firstPoint1.y, }; }

  const point3 = {
    x: point2.x + offset,
    y: point2.y
  };
  label.addShape('path', {
    attrs: {
      path: [
        ['M', point1.x, point1.y],
        ['L', point2.x, point2.y],
        ['L', point3.x, point3.y],
      ],
      stroke: mappingData.color,
      lineWidth: 1,
    },
  });
  label.addShape('text', {
    attrs: {
      x: point3.x + textOffset, // 加个偏移量
      y: point3.y,
      text: originData.count + ': ' + originData.count,
      textBaseline: 'middle',
      fill: '#000',
    },
  });
  preWidth += width;
}

} });

chart.render();

// 监听 element 上状态的变化来动态更新 Annotation 信息 chart.on('element:statechange', (ev) => { const { state, stateStatus, element } = ev.gEvent.originalEvent;

// 本示例只需要监听 active 的状态变化 if (state === 'active') { const data = element.data; if (stateStatus === 'active') { // 更新 Annotation updateAnnotation(data); } else { // 隐藏 Annotation clearAnnotation(); } } });

// 绘制 annotation let lastItem; function updateAnnotation(data) { if (data.item !== lastItem) { innerView.annotation().clear(true); innerView .annotation() .text({ // 第一部分: 显示 item 名称 position: ['50%', '50%'], content: data.item, style: { fontSize: 20, fill: '#8c8c8c', textAlign: 'center', }, offsetY: -20, }) .text({ // 第二部分: 显示 count 数据 position: ['50%', '50%'], content: data.count, style: { fontSize: 28, fill: '#8c8c8c', textAlign: 'center', }, offsetX: -10, offsetY: 20, }) .text({ // 第三部分: 显示 '台' 字样 position: ['50%', '50%'], content: '台', style: { fontSize: 20, fill: '#8c8c8c', textAlign: 'center', }, offsetY: 20, offsetX: 20, }); innerView.render(); lastItem = data.item; } }

// 清空 annotation function clearAnnotation() { innerView.annotation().clear(true); innerView.render(); lastItem = null; }

AntV G2 图表:自定义图例和交互式注释

原文地址: https://www.cveoy.top/t/topic/qmwn 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录