随心而记
847 字
4 分钟
使用 antv/g2 绘制双圈交集图形
2024-04-11
发布时间
分类

最近在开发新项目,但是这个项目由其他部门搭建的,使用了和我们原先不同的图表图。我们组原先使用的是 echarts 而这个项目使用的是 antv/g2 ,由于之前并没有使用过这个图表图,特此记录一下

Untitled

乍一看觉得这个实现起来并不复杂,使用韦恩图即可实现,随即上官网查看韦恩图示例,一步步修改配置,最终实现出来如下样子的图

Untitled

实现代码如下

chart
  .path()
  .data({
    type: 'inline',
    value: [
      { sets: ['A'], size: 15, label: '购买人数', value: 19992 },
      { sets: ['B'], size: 15, label: '会员总数', value: 29992 },
      { sets: ['A', 'B'], size: 6, label: '会员购买人数', value: 9992 },
    ],
    transform: [
      {
        type: 'venn',
      },
    ],
  })
  .encode('d', 'path')
  .encode('color', 'key')
  .encode('shape', 'hollow')
  .label({
    position: 'inside',
    text: (a) => `${a?.label}\n${formatNumOfOptions(a?.value, { digit: 0 })}`,
    style: {
      fontSize: 12,
      fontWeight: 500,
      fill: '#1890FF',
    },
  })
  .style('lineWidth', 1)
  .style('lineDash', [7, 4])
  .style('fill', (d) => {
    if (d.sets.length === 1) {
      if (d.sets[0] === 'A') {
        return '#3BA0FF';
      }
      return '#4DCB73';
    }
    return '#cdecf2';
  })
  .style('fillOpacity', 0.1)
  .legend(false)
  .tooltip(false);

观察上图很容易看出这与设计图有两处不同,分别是交汇处的线条颜色,以及 label 的位置

首先解决交汇处线条颜色,让它们在交汇处仍然用自身原本的颜色,使用 style 实现,代码如下

style('stroke', (d) => {
  if (d.key === 'A') {
    return '#1890FF';
  }
  if (d.key === 'B') {
    return '#4DCB73';
  }
  return 'transparent';
})

而解决 label 位置就稍微麻烦一些,需要自定义渲染 label ,查了下文档,发现自定义渲染使用 label.render 函数

Untitled

使用 render 函数自定义渲染 html 标签,配合 css 属性transform: translate 进行偏移最终实现标签的自定义位置,具体实现代码如下

render: (_, datum) => {
  if (datum.key === 'A') {
    return `
      <div style="font-weight: 500; color: #1890FF; transform: translate(-80px, -20px)">
        <p style="font-size: 12px">${datum.label}</p>
        <p style="font-size: 14px">${formatNumOfOptions(datum.value, { digit: 0 })}</p>
      </div>
    `;
  }
  if (datum.key === 'B') {
    return `
      <div style="font-weight: 500; color: #4DCB73; transform: translate(30px, -20px);">
      <p style="font-size: 12px">${datum.label}</p>
      <p style="font-size: 14px">${formatNumOfOptions(datum.value, { digit: 0 })}</p>
      </div>
    `;
  }
  return `
    <div style="font-weight: 500; color: #00BBFF; transform: translate(-36px, -20px);">
    <p style="font-size: 12px">${datum.label}</p>
    <p style="font-size: 14px">${formatNumOfOptions(datum.value, { digit: 0 })}</p>
    </div>
  `;
}

这时你会发现虽然 label 标签正确的进行了偏移但是在偏移原点时会出现蓝色方块,如下图所示

Untitled

这是因为使用了CSS来样式化标签,将 label.style.fill 属性删除即可,最终代码如下

const chart = new Chart({
      container: 'user-assets',
      height: 311,
    });

    chart
      .path()
      .data({
        type: 'inline',
        value: [
          { sets: ['A'], size: 15, label: '购买人数', value: 19992 },
          { sets: ['B'], size: 15, label: '会员总数', value: 29992 },
          { sets: ['A', 'B'], size: 6, label: '会员购买人数', value: 9992 },
        ],
        transform: [
          {
            type: 'venn',
          },
        ],
      })
      .encode('d', 'path')
      .encode('color', 'key')
      .encode('shape', 'hollow')
      .label({
        position: 'inside',
        text: (a) => `${a?.label}\n${formatNumOfOptions(a?.value, { digit: 0 })}`,
        style: {
          fontSize: 12,
        },
        render: (_, datum) => {
          if (datum.key === 'A') {
            return `
              <div style="font-weight: 500; color: #1890FF; transform: translate(-80px, -20px)">
                <p style="font-size: 12px">${datum.label}</p>
                <p style="font-size: 14px">${formatNumOfOptions(datum.value, { digit: 0 })}</p>
              </div>
            `;
          }
          if (datum.key === 'B') {
            return `
              <div style="font-weight: 500; color: #4DCB73; transform: translate(30px, -20px);">
              <p style="font-size: 12px">${datum.label}</p>
              <p style="font-size: 14px">${formatNumOfOptions(datum.value, { digit: 0 })}</p>
              </div>
            `;
          }
          return `
            <div style="font-weight: 500; color: #00BBFF; transform: translate(-36px, -20px);">
            <p style="font-size: 12px">${datum.label}</p>
            <p style="font-size: 14px">${formatNumOfOptions(datum.value, { digit: 0 })}</p>
            </div>
          `;
        },
      })
      .style('lineWidth', 1)
      .style('lineDash', [7, 4])
      .style('fill', (d) => {
        if (d.sets.length === 1) {
          if (d.sets[0] === 'A') {
            return '#3BA0FF';
          }
          return '#4DCB73';
        }
        return '#cdecf2';
      })
      // 交汇处描边的线条颜色维持它们各自的颜色
      .style('stroke', (d) => {
        if (d.key === 'A') {
          return '#1890FF';
        }
        if (d.key === 'B') {
          return '#4DCB73';
        }
        return 'transparent';
      })
      .style('fillOpacity', 0.1)
      .legend(false)
      .tooltip(false);
    chart.render();
使用 antv/g2 绘制双圈交集图形
https://www.promises.top/posts/drawing-venn-diagrams-with-antv-g2/
作者
发布于
2024-04-11
许可协议
CC BY-NC-SA 4.0