图例 Legend
概述
图例(Legend)插件用于展示图中元素的分类信息,支持节点、边、组合的分类信息展示。通过图例,用户可以快速感知到图中相关元素的分类信息,也可以通过点击对应图例项来快速定位到元素,提高用户的浏览效率。
使用场景
这一插件主要用于:
- 通过图例快速对元素进行分类
- 通过图例快速高亮定位到对应元素
基本用法
const data = {
nodes: [
{ id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },
{ id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },
],
edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],
};
const graph = new Graph({
data,
// 其他配置...
plugins: [
{
type: 'legend', // 插件类型为 legend
nodeField: 'cluster', // 用于节点分组的数组字段名称
edgeField: 'cluster', // 用于边分组的数组字段名称
},
],
});
配置项
| 属性 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| type | 插件类型 | string | legend |
✓ |
| key | 插件唯一标识符,用于后续更新 | string | - | |
| trigger | 图例项触发对应项高亮的方式: - hover:鼠标移入图例项时触发 - click:鼠标点击图例项时触发 |
hover | click |
hover |
|
| position | 图例在画布中的相对位置,可选值 | CardinalPlacement | bottom |
|
| container | 图例挂载的容器,无则挂载到 Graph 所在容器 | HTMLElement | string | - | |
| className | 图例画布类名,传入外置容器时不生效 | string | - | |
| containerStyle | 图例的容器样式,传入外置容器时不生效 | CSSStyleDeclaration | - | |
| nodeField | 节点分类标识 | string | (item: ElementDatum) => string | - | |
| edgeField | 边分类标识 | string | (item: ElementDatum) => string | - | |
| comboField | 组合分类标识 | string | (item: ElementDatum) => string | - | |
| orientation | 图例项的布局方向: - horizontal:水平方向 - vertical:垂直方向 |
horizontal | vertical |
‘horizontal’ | |
| layout | 布局方式: - flex:弹性布局 - grid:网格布局 |
flex | grid |
flex |
|
| showTitle | 是否显示标题 | boolean | false | |
| titleText | 标题内容 | string | “” | |
| x | 图例在画布中的相对的横向位置,优先级高于position | number | - | |
| y | 图例在画布中的相对的纵向位置,优先级高于position | number | - | |
| width | 图例的宽度 | number | 240 | |
| height | 图例的高度 | number | 160 | |
| itemSpacing | 图例项的文本和对应标记之间的间距 | number | 4 | |
| rowPadding | 图例中每行之间的间距 | number | 10 | |
| colPadding | 图例中每列之间的间距 | number | 10 | |
| itemMarkerSize | 图例项标记的大小 | number | 16 | |
| itemLabelFontSize | 图例项文本的字体大小 | number | 16 | |
| gridCol | 图例项在宽度允许情况下的最大列数 | number | - | |
| gridRow | 图例项在高度允许情况下的最大行数 | number | - |
CardinalPlacement
position 属性支持以下值:
'top-left':左上角'top-right':右上角'bottom-left':左下角'bottom-right':右下角'left-top':左侧靠上'left-bottom':左侧靠下'right-top':右侧靠上'right-bottom':右侧靠下
代码示例
基础图例
const data = {
nodes: [
{ id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },
{ id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },
],
edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],
};
const graph = new Graph({
// 其他配置...
plugins: [
{
type: 'legend', // 插件类型为 legend
nodeField: 'cluster', // 用于节点分组的数组字段名称
edgeField: 'cluster', // 用于边分组的数组字段名称
},
],
});
自定义图例位置
const data = {
nodes: [
{ id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },
{ id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },
],
edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],
};
const graph = new Graph({
data,
// 其他配置...
plugins: [
{
type: 'legend',
nodeField: 'cluster',
edgeField: 'cluster',
// 可以通过 position 快捷的来指定位置
// position: "top-left",
// 也可以通过x,y来更加灵活的控制图例的位置
x: 20,
y: 20,
},
],
});
自定义图例项布局
const data = {
nodes: [
{ id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },
{ id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },
],
edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],
};
const graph = new Graph({
data,
// 其他配置...
plugins: [
{
type: 'legend',
nodeField: 'cluster',
edgeField: 'cluster',
layout: 'flex',
// 控制只显示一行
gridRow: 1,
// 控制一行显示10列,当列宽不足时会显示翻页按钮
gridCol: 10,
},
],
});
常见问题
1. 设置了 orientation 无效?
orientation主要控制布局的方向,具体展示一行多列还是一列多行,主要通过 gridRow 以及gridCol来控制,例如想要看起来像是竖向的图例项,则可以通过这样配置:
plugins: [
{
type: 'legend',
nodeField: 'cluster',
edgeField: 'cluster',
layout: "flex",
// 控制一行显示1列
gridCol:1,
// 控制显示最多20行
gridRow: 20,
},
],
这样就变成了只有一列的图例,符合视觉上的竖向排列。
2. 如何动态更新工具栏?
可以使用 updatePlugin 方法动态更新工具栏:
const graph = new Graph({
data,
// 其他配置...
plugins: [
{
type: 'legend',
key: 'my-legend',
nodeField: 'cluster',
edgeField: 'cluster',
},
],
});
// 更新图例位置
graph.updatePlugin({
key: 'my-legend',
position: 'bottom-right',
});
实际案例
import { Graph } from '@antv/g6';
const data = {
nodes: [
{ id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },
{ id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },
{ id: 'node-3', type: 'triangle', data: { cluster: 'node-type3' } },
{ id: 'node-4', type: 'diamond', data: { cluster: 'node-type4' } },
],
edges: [
{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } },
{ source: 'node-1', target: 'node-4', data: { cluster: 'edge-type2' } },
{ source: 'node-3', target: 'node-4' },
{ source: 'node-2', target: 'node-4', data: { cluster: 'edge-type3' } },
],
};
const graph = new Graph({
container: 'container',
data,
node: {
style: { size: 32 },
palette: {
field: 'cluster',
},
},
layout: {
type: 'force',
},
plugins: [
{
type: 'legend',
nodeField: 'cluster',
edgeField: 'cluster',
},
],
});
graph.render();
import { Graph } from '@antv/g6';
import GUI from 'lil-gui';
export function addPanel(renderPanel: (gui: GUI) => void) {
const gui = new GUI({ container: document.body });
gui.title('Control');
Object.assign(gui.domElement.style, { position: 'absolute', top: '0', right: '0', zIndex: '10' });
renderPanel(gui);
addEventListener('pagehide', () => gui.destroy(), { once: true });
}
export async function createGraph(
options: ConstructorParameters<typeof Graph>[0],
size: { width?: number; height?: number } = {},
renderPanel?: (gui: GUI, graph: Graph) => void,
) {
const container = document.createElement('div');
Object.assign(container.style, {
width: '100%',
maxWidth: `${size.width || 600}px`,
height: `${size.height || 400}px`,
});
document.getElementById('container')!.append(container);
const graph = new Graph({ ...size, ...options, width: container.clientWidth, container, autoResize: true });
addEventListener('pagehide', () => graph.destroy(), { once: true });
await graph.render();
if (renderPanel) addPanel((gui) => renderPanel(gui, graph));
return container;
}