// 建立 G6 图实例 const graph = new G6.Graph({ container: 'mountNode', // 指定图画布的容器 id,与第 9 行的容器对应 // 画布宽高 width: 800, height: 500, }); // 读取数据 graph.data(data); // 渲染图 graph.render(); //监听 graph.on()
Graph 对象的生命周期为:初始化 —> 加载数据 —> 渲染 —> 更新 —> 销毁。node
上面代码中实例化 Graph 的部分使用了三个必要的配置项:git
container
width
、height
renderer
fitView
fitViewPadding
fitCenter
defaultNode
defaultEdge
nodeStateStyles
edgeStateStyles
layout
modes
animate
animateCfg
plugins
每一个图元素由图形(Shape) 组成,且都会有本身的惟一关键图形(keyShape)。web
获取实例的属性值。算法
更新实例的单个绘图属性。canvas
批量更新实例绘图属性。api
表明元素的图形数组
变换dom
图的元素(Item)包含图上的节点 Node 、边 Edge 和 Combo 三大类。ide
style
字段对象进行配置,和元素的关键图形相关,例如 fill
,stroke
。id
、type
,不能在元素状态改变是进行改变,可经过 graph.updateItem 进行手动更新。节点svg
G6 的内置节点包括 circle,rect,ellipse,diamond,triangle,star,image,modelRect。这些内置节点的默认样式分别以下图所示。
定义方式
// 1 defaultNode: { type: 'circle', // 其余配置 } //2 graph.node((node) => { return { id: node.id, type: 'rect', style: { fill: 'blue', }, }; }); graph.data(data); graph.render(); //3 const data = { nodes: [ { id: 'node_circle', x: 100, y: 100, type: 'circle', label: 'circle', },] }
自定义
G6.registerNode( 'nodeName', { options: { style: {}, stateStyles: { hover: {}, selected: {}, }, }, /** * 绘制节点,包含文本 * @param {Object} cfg 节点的配置项 * @param {G.Group} group 图形分组,节点中图形对象的容器 * @return {G.Shape} 返回一个绘制的图形做为 keyShape,经过 node.get('keyShape') 能够获取。 * 关于 keyShape 可参考文档 核心概念-节点/边/Combo-图形 Shape 与 keyShape */ draw(cfg, group) {}, /** * 绘制后的附加操做,默认没有任何操做 * @param {Object} cfg 节点的配置项 * @param {G.Group} group 图形分组,节点中图形对象的容器 */ afterDraw(cfg, group) {}, /** * 更新节点,包含文本 * @override * @param {Object} cfg 节点的配置项 * @param {Node} node 节点 */ update(cfg, node) {}, /** * 更新节点后的操做,通常同 afterDraw 配合使用 * @override * @param {Object} cfg 节点的配置项 * @param {Node} node 节点 */ afterUpdate(cfg, node) {}, /** * 响应节点的状态变化。 * 在须要使用动画来响应状态变化时须要被复写,其余样式的响应参见下文说起的 [配置状态样式] 文档 * @param {String} name 状态名称 * @param {Object} value 状态值 * @param {Node} node 节点 */ setState(name, value, node) {}, /** * 获取锚点(相关边的连入点) * @param {Object} cfg 节点的配置项 * @return {Array|null} 锚点(相关边的连入点)的数组,若是为 null,则没有控制点 */ getAnchorPoints(cfg) {}, }, // 继承内置节点类型的名字,例如基类 'single-node',或 'circle', 'rect' 等 // 当不指定该参数则表明不继承任何内置节点类型 extendedNodeName, );
链接方式
// 接入点 anchorPoints: [ [0, 1], [0.5, 1], ],
jsx写法
<[group|shape] [key]="value" style={{ [key]: value }}> <[more tag] /> ... <text>value</text> </[group|shape]>
边
定义方式: 与节点相似
箭头:
//默认 style: { endArrow: true, startArrow: true } //内置 6种 endArrow: { // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应) path: G6.Arrow.triangle(10, 20, 25), d: 25 } // 自定义箭头 // 只有内置箭头和自定义箭头能够配置样式 style: { endArrow: { path: 'M 0,0 L 20,10 L 20,-10 Z', d: 5, fill: '#f00', stroke: '#0f0', opacity: 0.5, lineWidth: 3, // ... }, }
自定义:同节点
combo
G6 的内置 Combo 包括 circle 和 rect 两种类型
对于熟悉图可视化类库的用户来讲,节点分组是很是实用的一个功能
G6 已经存在一个节点分组 Node Group 功能,但它的机制没法支持一些较复杂的功能,例如:带有节点分组的图布局、自定义 Combo、嵌套节点分组的均匀 padding、节点与分组的边、分组与分组的边、空的节点分组等
{ nodes: [ { id: 'node1', comboId: 'comboA' // node1 属于 comboA }, { id: 'node2', comboId: 'comboB' // node2 属于 comboB }, { id: 'node3' // node3 不属于任何 combo }, // ... ], edges: [ // ... ], combos: [ { // 定义 comboA id: 'comboA', label: 'A', parentId: 'comboC' }, { // 定义 comboB id: 'comboB', parentId: 'comboB' }, { // 定义 comboC,这是一个空的 combo id: 'comboC' }, // ... ] }
其余内容:相似与节点
高级样式
背景
defaultNode: { position: 'left', style: { background: { fill: '#ffffff', stroke: 'green', padding: [3, 2, 3, 2], radius: 2, lineWidth: 3, }, },
三种方式更新文本样式
// 1. 实例化默认 defaultNode: { type: 'node', labelCfg: { style: { fill: '#fff', fontSize: 14, }, }, }, // 2.数据指定 const data = { nodes: [ { id: 'node1', label: 'node1', labelCfg: { style: { fill: '#fff', fontSize: 12, }, }, }, ], }; // 3.update/updateItem graph.updateItem(node, { // 节点的样式 style: { stroke: 'blue', }, // 节点上文本的样式 labelCfg: { style: { fill: '#fff', fontSize: 12, }, }, });
渐变色/纹理
操做
更新样式
更新节点边: 三种方式
层级
全部节点会绘制在全部边的上层
先绘制图形在后绘制图形后边
toFront()
与 toBack()
显示/隐藏
show()/hide()
多条边
自定义边 edgeType
锁定/解锁
lock()
、unlock()
和 hasLocked()
不可拖动
不可缩放
通常布局
树图
布局切换
updateLayout(params)
:布局方法或参数的切换;graph.updateLayout({ type: 'force', // 布局名称 preventOverlap: true, // 布局参数,是否容许重叠 nodeSize: 40, // 布局参数,节点大小,用于判断节点是否重叠 linkDistance: 100, // 布局参数,边长 }); * `changeData()`:数据的切换。 graph.changeData(data2);
子图
子图布局独立与全局布局的思路,与 graph 不挂钩,直接使用实例化布局方法的方式,灌入子图数据,经过布局将位置写到相应数据中。这种机制还可供外部的全局布局使用,即便不用 G6 渲染,也能够计算节点布局后的位置
// 实例化布局 const subgraphLayout = new G6.Layout['force']({ center: [500, 450], }); // 初始化布局,灌入子图数据 subgraphLayout.init({ nodes: subGraphNodes, edges: subGraphEdges, }); // 执行布局 subgraphLayout.execute(); // 图实例根据数据更新节点位置 graph.positionsAnimate();
webworker
在大规模图可视化中,布局算法每每须要较大的计算量。
workerEnabled: true, // 开启 Web-Worker
自定义布局
getDefaultCfg() { return {}; }, /** * 初始化 * @param {object} data 数据 */ init(data) {}, /** * 执行布局 */ execute() {}, /** * 根据传入的数据进行布局 * @param {object} data 数据 */ layout(data) {}, /** * 更新布局配置,但不执行布局 * @param {object} cfg 须要更新的配置项 */ updateCfg(cfg) {}, /** * 销毁 */ destroy() {}, });
布局预测
import { GraphLayoutPredict } from '@antv/vis-predict-engine' const { predictLayout, confidence } = await GraphLayoutPredict.predict(data); const graph = new G6.Graph({ // 省略其余配置 layout: { type: predictLayout } })
监听/绑定
mousedown
,mouseup
,click
,mouseenter
,mouseleave
等;node:mousedown
,edge:click
等,以 type:eventName
为事件名称;时机事件:
beforeadditem
,afteradditem
等;beforerefreshitem
与 afterrefreshitem
;beforelayout
与 afterlayout
。graph.on('click', (ev) => { const shape = ev.target; const item = ev.item; if (item) { const type = item.getType(); } }); graph.on('node:click', (ev) => { const shape = ev.target; const node = ev.item; });
内置 behavior
ehavior 是 G6 提供的定义图上交互事件的机制。
G6 目前共提供了如下 14 个内置的 Behavior。
drag-combo
collapse-expand-combo
drag-canvas
zoom-canvas
drag-node
click-select
tooltip
edge-tooltip
activate-relations
brush-select
lasso-select
collapse-expand
create-edge
shortcuts-call
在交互行为上, G6 主要考虑了三个场景:
在这些场景中只要用户可能没法一眼看清楚全部须要的信息,都须要进行交互,例如:
modes: { // 支持的 behavior default: ['drag-canvas', 'zoom-canvas'], edit: ['click-select'], },
// 解绑目前图模式的全部事件监听;
// 生成新的 Behavior ,进行事件初始化;
// 绑定新的行为对应的事件监听。
graph.setMode('edit');
graph.addBehaviors
graph.removeBehaviors
状态State
判断是否该使用 state 的原则很简单,从交互和业务两个层面来看:
知足上述条件其一,则应该使用 state。
在 G6 中,有两种方式配置不一样状态的样式:
nodeStateStyles
和 edgeStateStyles
对象定义;stateStyles
对象中定义状态;setItemState
clearItemStates
updateItem
graph.setItemState(item, stateName, stateValue) graph.clearItemStates(item, 'selected');
// 实例化 nodeStateStyles: { }, //数据 stateStyles: { }, //updataItem stateStyles: { // 修改 hover 状态下的样式 hover: { opacity: 0.1, // 修改 name 为 'node-label' 的子图形 hover 状态下的样式 'node-text': { stroke: 'blue', }, }, }, //优先级 item.hasState('active');
// 实例化 Image Minimap 插件 const imageMinimap = new G6.ImageMinimap({ width: 200, graphImg: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*eD7nT6tmYgAAAAAAAAAAAABkARQnAQ' }); const graph = new G6.Graph({ //... 其余配置项 plugins: [imageMinimap], // 配置 imageMinimap 插件 });
计算nodes,edges x/y
全局
//全局 animate: true, // Boolean,切换布局时是否使用动画过分,默认为 false graph.updateLayout(cfg) 布局的变化 graph.changeData() 数据的变化
元素
//开始 shape.animate( (ratio) => { // 每一帧的操做,入参 ratio:这一帧的比例值(Number)。返回值:这一帧须要变化的参数集(Object)。 // 先变大、再变小 const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10; let radius = cfg.size; if (isNaN(radius)) radius = radius[0]; // 返回这一帧须要变化的参数集,这里只包含了半径 return { r: radius / 2 + diff, }; }, { // 动画重复 repeat: true, duration: 3000, easing: 'easeCubic', }, ); // 一次动画持续的时长为 3000,动画效果为 'easeCubic' //结束 shape.stopAnimate();