sankey

桑基图 (Sankey Diagram),是一种特定类型的流图,用于描述一组值到另一组值的流向。桑基图的特点如下: 起始流量和结束流量相同,所有主支宽度的总和与所有分出去的分支宽度总和相等,保持能量的平衡; 在内部,不同的线条代表了不同的流量分流情况,它的宽度成比例地显示此分支占有的流量; 节点不同的宽度代表了特定状态下的流量大小。 桑基图通常应用于能源、材料成分、金融等数据的可视化分析。

开始使用

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

const chart = new Chart({
  container: 'container',
  padding: 10,
});

chart.options({
  type: 'sankey',
  data: {
    type: 'fetch',
    value: 'https://assets.antv.antgroup.com/g2/energy.json',
    transform: [
      {
        type: 'custom',
        callback: (data) => ({ links: data }),
      },
    ],
  },
  layout: {
    nodeAlign: 'center',
    nodePadding: 0.03,
  },
  scale: { color: { range: schemeTableau10 } },
  style: {
    labelSpacing: 3,
    labelFontWeight: 'bold',
    nodeStrokeWidth: 1.2,
    linkFillOpacity: 0.4,
  },
});

chart.render();

G2 中布局(Layout) 用于指定一些有特定布局函数标记的布局方法的参数,比如 Snakey, WordCloud, ForceGraph 等。

({
  type: 'sankey',
  layout: {
    nodeAlign: 'center',
    nodePadding: 0.03,
  },
});
// API
chart.options({
  type: 'sankey',
  layout: { nodeAlign: 'center', nodePadding: 0.03 },
});

数据更新

桑基图支持动态更新数据,使用 G2 内置的 API changeData() 更新数据:

const newData = {
  links: [
    { source: 'A', target: 'B', value: 10 },
    { source: 'A', target: 'C', value: 15 },
    { source: 'B', target: 'D', value: 8 },
    { source: 'C', target: 'D', value: 12 },
  ],
};
chart.changeData({ type: 'inline', value: newData });

语法糖(推荐)

同时 G2 也提供了便捷的语法糖,你可以像配置其他图表一样,直接传递数组数据:

const newData = [
  { source: 'A', target: 'X', value: 10 },
  { source: 'A', target: 'Y', value: 15 },
  { source: 'B', target: 'X', value: 20 },
  { source: 'B', target: 'Y', value: 25 },
];

// 直接传递数组
chart.changeData(newData);

空数据处理

当传入空数组时或者不传入 links,图表将显示为空白状态:

// 清空图表 - 图表将显示为空白状态
chart.changeData([]);
// 或者
chart.changeData({ links: [] });

桑基图数据更新示例

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

const chart = new Chart({ container: 'container' });

const initialData = [
  { source: 'A', target: 'X', value: 10 },
  { source: 'A', target: 'Y', value: 15 },
  { source: 'B', target: 'X', value: 20 },
];

chart.options({
  type: 'sankey',
  data: {
    type: 'inline',
    value: initialData,
  },
});

chart.render();

// 点击事件:随机更新数据
chart.on('element:click', () => {
  const randomData = initialData.map((d) => ({
    ...d,
    value: Math.random() * 30 + 5,
  }));

  // 使用简化语法更新数据
  chart.changeData(randomData);
});

选项

属性 描述 类型 默认值
tooltip 配置桑基图的 tooltip,详见 tooltip 配置 object 详见 tooltip 配置
layout 配置桑基图的布局方式,详见 layout 配置 object 详见 layout 配置
style 配置图形样式和标签样式,详见 style 配置 object 详见 style 配置
nodeLabels 自定义节点数据标签的配置,详见 nodeLabels 配置 label[] []
linkLabels 自定义连接线数据标签的配置,详见 linkLabels 配置 label[] []

tooltip

桑基图作为复合图形,配置 tooltip 的时候需要区分节点和连接线。

title

不同于单一标记配置 title,桑基图需要分别配置 nodeTitlelinkTitle

({
  tooltip: {
    nodeTitle: (d) => d.key,
    linkTitle: (d) => 'link',
  },
});

items

不同于单一标记配置 items,桑基图需要同时配置 nodeItemslinkItems

和一般的 items 一样, nodeItemslinkItems 也支持自定义配置:

({
  tooltip: {
    nodeItems: [
      (d, index, data, column) => {
        return {
          color: 'red', // 指定 item 的颜色
          name: '节点', // 指定 item 的名字
          value: d.key, // 使用 y 通道的值
          content: '节点自定义属性',
        };
      },
    ],
    linkItems: [
      (d, index, data, column) => {
        return {
          color: 'red', // 指定 item 的颜色
          name: '连接线', // 指定 item 的名字
          value: `${d.source.key}-${d.target.key}`, // 使用 y 通道的值
          content: '连接线自定义属性',
        };
      },
    ],
  },
});

💡 桑基图怎么使用 data 中的补充属性实现自定义 tooltip 的展示?

和一般 Mark 自定义 tooltip 交互的方法类似,先在图形的 tooltip 里传入自定义属性,然后在 interaction 里使用。

示例:

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

const chart = new Chart({
  container: 'container',
});

const data = {
  nodes: [
    { id: 'a', key: '首页', des: '节点自定义属性' },
    { id: 'b', key: '页面1', des: '节点自定义属性' },
    { id: 'b_1', key: '页面1', des: '节点自定义属性' },
    { id: 'c', key: '页面2', des: '节点自定义属性' },
    { id: 'c_1', key: '页面2', des: '节点自定义属性' },
    { id: 'd', key: '页面3', des: '节点自定义属性' },
    { id: 'd_1', key: '页面3', des: '节点自定义属性' },
  ],
  links: [
    { source: 'a', target: 'b', value: 100 },
    { source: 'b', target: 'c', value: 80 },
    { source: 'b', target: 'd', value: 20 },
    { source: 'c', target: 'b_1', value: 80 },
    { source: 'b_1', target: 'c_1', value: 40 },
    { source: 'b_1', target: 'd_1', value: 40 },
  ],
};

chart.options({
  type: 'sankey',
  width: 900,
  height: 600,
  data: {
    value: data,
    transform: [
      {
        type: 'custom',
        callback: (data) => ({
          nodes: data.nodes,
          links: data.links,
        }),
      },
    ],
  },
  tooltip: {
    nodeItems: [
      (d, index, data, column) => {
        return {
          content: d.des,
        };
      },
    ],
    linkItems: [
      (d, index, data, column) => {
        return {
          color: 'red', // 指定 item 的颜色
          name: '连接线', // 指定 item 的名字
          value: `${d.source.key}-${d.target.key}`, // 使用 y 通道的值
          content: '连接线自定义属性',
        };
      },
    ],
  },
  layout: {
    nodeId: (d) => d.id,
    nodeAlign: 'center',
    nodePadding: 0.03,
    iterations: 25,
  },
  style: {
    labelSpacing: 3,
    labelFontWeight: 'bold',
    // linkFillOpacity: 0.2,
    // linkFill: '#3F96FF',
  },
  interaction: {
    tooltip: {
      render: (e, { items, title }) => {
        return `<div>${items[0].content}</div>`;
      },
    },
  },
});

chart.render();

layout

桑基图的布局方式。具体配置项如下:

nodeId

<description>optional function </description>

回调的方式为:(node: any) => string,如果未指定 nodeId,默认为 (node) => node.key

节点绑定字段,在布局中作为唯一标识。

💡 桑基图不支持成环,那在页面流向图这种会出现重复节点的情况应该怎么配置?

对于多次出现的节点,设置 id 作为唯一标识,并配置 nodeId 的回调方法为 (node) => node.id

示例:

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

const chart = new Chart({
  container: 'container',
});

const data = {
  nodes: [
    { id: 'a', key: '首页' },
    { id: 'b', key: '页面1' },
    { id: 'b_1', key: '页面1' },
    { id: 'c', key: '页面2' },
    { id: 'c_1', key: '页面2' },
    { id: 'd', key: '页面3' },
    { id: 'd_1', key: '页面3' },
  ],
  links: [
    { source: 'a', target: 'b', value: 100 },
    { source: 'b', target: 'c', value: 80 },
    { source: 'b', target: 'd', value: 20 },
    { source: 'c', target: 'b_1', value: 80 },
    { source: 'b_1', target: 'c_1', value: 40 },
    { source: 'b_1', target: 'd_1', value: 40 },
  ],
};

chart.options({
  type: 'sankey',
  width: 900,
  height: 600,
  data: {
    value: data,
    transform: [
      {
        type: 'custom',
        callback: (data) => ({
          nodes: data.nodes,
          links: data.links,
        }),
      },
    ],
  },
  layout: {
    nodeId: (d) => d.id,
    nodeAlign: 'center',
    nodePadding: 0.03,
    iterations: 25,
  },
  style: {
    labelSpacing: 3,
    labelFontWeight: 'bold',
    linkFillOpacity: 0.2,
    linkFill: '#3F96FF',
  },
});

chart.render();

nodeSort

<description>optional function </description>

回调的方式为:((a: any, b: any) => number)

节点排序方式。如果未指定 nodeSort ,则返回当前节点排序方法,默认为 undefined,表示每列内的节点垂直顺序将由布局自动确定。如果 nodeSortnull,则顺序由输入固定。否则,由指定的排序函数确定顺序;该函数传递两个节点,如果第一个节点应位于第二个节点上方,则必须返回小于 0 的值,如果第二个节点应位于第一个节点上方,则必须返回大于 0 的值,如果未指定顺序,则返回 0。

linkSort

<description> optional function </description>

回调的方式为:((a: any, b: any) => number)

连接线排序方式。如果未指定 linkSort ,则返回当前连接线排序方法,默认为 undefined,表示每个节点内的连接线的垂直顺序将由布局自动确定。如果 linkSortnull,则顺序由输入固定。否则,由指定的排序函数确定顺序;该函数传递两个连接线,如果第一个连接线应位于第二个连接线上方,则必须返回小于 0 的值,如果第二个连接线应位于第一个连接线上方,则必须返回大于 0 的值,如果未指定顺序,则返回 0。

nodeAlign

<description>optional stringfunction </description>

内置支持的类型有: 'left' | 'right' | 'center' | 'justify'

回调的方式为:((node: any, n: number) => number

当前节点的对齐方法。除了内置的几种类型外,还可以传递当前节点和图的总深度 n (最大的节点深度+1 ),并且必须返回 0n - 1 之间的整数,指示节点在生成图中所需的水平位置。

nodeWidth

<description>optional number </description>

节点的宽度。默认为 0.02

nodePadding

<description>optional number </description>

节点的间距。默认为 0.02

nodeDepth

<description>optional function </description>

回调的方式为:(datum: any, maxDepth: number) => number

节点的深度。

iterations

<description>optional number </description>

布局计算迭代次数,默认为 6, 次数越多,布局越合理。

更多 layout 配置,详见 d3-sankey

style

默认 style 配置:

({
  // label
  labelText: (d) => d.key,
  labelSpacing: 5,
  labelFontSize: 10,
  // node
  nodeStroke: '#000',
  // link
  linkFillOpacity: 0.5,
  linkStroke: undefined,
});

复合图形标记需要通过不同的前缀来区分图形的配置。

  • <label>: 配置数据标签的前缀。
属性名 类型 介绍
labelText (d: any) => string 桑基图配置默认的数据标签的值,默认为 (d) => d.key
labelSpacing number 桑基图配置数据标签的间距,默认为 5
labelFontSize number 桑基图数据标签文字大小
labelFontFamily string 桑基图数据标签文字字体
labelFontWeight number 桑基图数据标签字体粗细
labelLineHeight number 桑基图数据标签文字的行高
labelTextAlign string 设置桑基图数据标签文本内容的当前对齐方式, 支持的属性:center | end | left | right | start,默认值为start
labelTextBaseline string 设置桑基图数据标签在绘制文本时使用的当前文本基线, 支持的属性:top | middle | bottom | alphabetic | hanging。默认值为bottom
labelFill string 桑基图数据标签文字的填充色
labelFillOpacity number 桑基图数据标签文字的填充透明度
labelStroke string 桑基图数据标签文字的描边
labelLineWidth number 桑基图数据标签文字描边的宽度
labelLineDash [number,number] 桑基图数据标签描边的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0,0]的效果为没有描边。
labelStrokeOpacity number 桑基图数据标签描边透明度
labelOpacity number 桑基图数据标签文字的整体透明度
labelShadowColor string 桑基图数据标签文字阴影颜色
labelShadowBlur number 桑基图数据标签文字阴影的高斯模糊系数
labelShadowOffsetX number 设置桑基图数据标签阴影距文字的水平距离
labelShadowOffsetY number 设置桑基图数据标签阴影距文字的垂直距离
labelCursor string 桑基图数据标签鼠标样式。同 css 的鼠标样式,默认 ‘default’。
  • <node>: 配置节点的前缀。
属性名 类型 介绍
nodeFill string 桑基图节点填充色
nodeFillOpacity number 桑基图节点填充透明度
nodeStroke string 桑基图节点的描边
nodeStrokeOpacity number 桑基图节点描边透明度
nodeLineWidth number 桑基图节点描边的宽度
nodeLineDash [number,number] 桑基图节点描边的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0,0]的效果为没有描边。
nodeOpacity number 桑基图节点的整体透明度
nodeShadowColor string 桑基图节点阴影颜色
nodeShadowBlur number 桑基图节点阴影的高斯模糊系数
nodeShadowOffsetX number 设置阴影距桑基图节点的水平距离
nodeShadowOffsetY number 设置阴影距桑基图节点的垂直距离
nodeCursor string 桑基图节点鼠标样式。同 css 的鼠标样式,默认 ‘default’。
  • <link>: 配置连接线的前缀。
属性名 类型 介绍
linkFill string 桑基图连接线填充色
linkFillOpacity number 桑基图连接线填充透明度
linkStroke string 桑基图连接线的描边
linkStrokeOpacity number 桑基图连接线描边透明度
linkLineWidth number 桑基图连接线描边的宽度
linkLineDash [number,number] 桑基图连接线描边的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0,0]的效果为没有描边。
linkOpacity number 桑基图连接线的整体透明度
linkShadowColor string 桑基图连接线阴影颜色
linkShadowBlur number 桑基图连接线阴影的高斯模糊系数
linkShadowOffsetX number 设置阴影距桑基图连接线的水平距离
linkShadowOffsetY number 设置阴影距桑基图连接线的垂直距离
linkCursor string 桑基图连接线鼠标样式。同 css 的鼠标样式,默认 ‘default’。

nodeLabels

<description>optional Label[] </description>

内置数据标签的配置如下。

({
  labels: [
    {
      text,
      dx: (d) => (d.x[0] < 0.5 ? spacing : -spacing),
      ...labelStyle, // 用户传入的数据标签自定义样式
    },
    ...nodeLabels, // 用户传入的自定义数据标签
  ],
});

除了节点内置的数据标签以外,你还可以自定义节点数据标签的配置。

({
  nodeLabels: [
    {
      text: (d) => d.key,
      fontSize: 10, // 注意!!!这里的绘图属性不再需要加label前缀
      fill: 'red',
    },
  ],
});

linkLabels

<description>optional Label[] </description>

连接线没有内置的数据标签,你可以自定义连接线数据标签的配置。

({
  linkLabels: [
    {
      text: (d) => d.key,
      fontSize: 10, // 注意!!!这里的绘图属性不再需要加label前缀
      fill: 'yellow',
    },
  ],
});

state

state 配置和 style 类似,使用不同的前缀来区分不同的图形配置,没有前缀的配置两种图形都会生效。

示例:

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

(() => {
  const chart = new G2.Chart({ container: 'container' });

  const data = {
    links: [
      { source: 'a', target: 'b', value: 100 },
      { source: 'b', target: 'c', value: 80 },
      { source: 'b', target: 'd', value: 20 },
      { source: 'c', target: 'b_1', value: 80 },
      { source: 'b_1', target: 'c_1', value: 40 },
      { source: 'b_1', target: 'd_1', value: 40 },
    ],
  };

  chart.options({
    type: 'sankey',
    width: 900,
    height: 600,
    data: {
      value: data,
    },
    style: {
      labelSpacing: 3,
      labelFontWeight: 'bold',
      linkFillOpacity: 0.5,
      nodeFillOpacity: 0.5, //默认透明度都是 0.5
    },
    state: {
      active: {
        fillOpacity: 0.8, // 鼠标悬浮状态下透明度都是 0.8
        linkFill: 'red', // link 会变成红色
        nodeFill: 'blue', // node 会变成蓝色
      },
      inactive: {
        linkFillOpacity: 0.4,
        nodeFillOpacity: 0.2, // node 颜色比 link 浅
      },
    },
    interaction: {
      elementHighlight: true,
    },
  });

  chart.render();

  return chart.getContainer();
})();
全站搜索
文档与示例输入关键词开始搜索

输入关键词,查找文档、API 与示例

选择打开Esc关闭