Graphin——如何实现基础的节点、边的自定义交互?

500 阅读3分钟

Graphin的特殊之处

首先我们随便按照官方文档复制一份代码出来跑一跑。

image.png 我们渲染好一个画板在页面后,打开检查,会发现一个有趣的现象:所有组件全部被隐式集成在仅仅一个标签:< canvas > 里面了,这和我们之前经常见到的neo4j等图分析画板并不一样

其实这种方式新手上手还是比较容易的,几行代码就可以实现一个惟妙惟肖的图出来。

image.png

image.png

自定义交互的目标

那么问题来了,所有点、边全部都在canvas里,也就是说我们没有办法直接获得任何node或edge的实例标签,那怎么针对每个独特的边和点执行特定操作呢?

对Grphin实现逻辑的理解

在官方文档里,我们可以发现所谓的ToolBar、ToolTip,以及各种自定义的右键ContextBar,其实都有着一种十分相似且统一的编码方式:自定义组件,将组件引入Grphin,canvas内部自动渲染这些组件所带有的“逻辑”

ToolBar这种东西是阿里已经封装好的组件了,那当我们希望自定义一些组件时,在return函数之前就应当定义好整个组件的内容,随后再加入Graphin内部。

例子:单击特定节点触发特定函数——聚焦&&显示节点信息

那么,在这里,我加入了一个自定义的ClickBehavior,即点击后触发的事件 该组件作为一个函数,我们会发现它只是作为一个“函数逻辑”被加入到canvas当中,不return不render任何标签,也就是说这一函数逻辑被编写后被Graphin采用了 Graphin是如何知道自己要不要采用一个自定义交互(Behavior)的呢?

  1. 第一种方法就是通过注册交互,这一种适合实现简单的自定义交互,例如官方文档中的案例
import React from 'react';
import Graphin, { IG6GraphEvent, Utils, GraphinData } from '@antv/graphin';
import { message } from 'antd';
import { INode, NodeConfig } from '@antv/g6';
const data: GraphinData = Utils.mock(8).circle().graphin();
Graphin.registerBehavior('sampleBehavior', {
  getEvents() {
    return {
      'node:click': 'onClick',
    };
  },
  onClick(evt: IG6GraphEvent) {
    const node = evt.item as INode;
    const model = node.getModel() as NodeConfig;
    message.info(model.id);
    // TODO
  },
});
/**
 * @example
 * https://g6.antv.vision/zh/docs/api/Behavior
 */
export default () => {
  return <Graphin data={data} layout={{ type: 'concentric' }} modes={{ default: ['sampleBehavior'] }} />;
};

并在graphin组件 2. 第二种方法就是像下文一样利用useContext(GraphinContext)解决Graphin和ClickBehavior之间的组件传值

Child.js

我自定义了一些点和边,并为点和边建立了点击事件

import React, { useEffect, useContext, useState } from 'react';
import Graphin, { GraphinContext, Behaviors } from '@antv/graphin';
import { Toolbar } from '@antv/graphin-components';

const layout = {
    type: 'graphin-force',
};
const { DragNodeWithForce } = Behaviors;
const ClickBehavior = () => {
    // 解决canvas和click组件传参问题
    const { graph, apis } = useContext(GraphinContext);
    useEffect(() => {
        // 初始化聚焦到0
        apis.focusNodeById('node0');
        const nodeClick = (evt) => {
            const node = evt.item;
            const model = node.getModel();
            apis.focusNodeById(model.id);
            console.log(model.id)
        };
        const edgeClick = (evt) =>{
            const edge = evt.item;
            const model = edge.getModel();
            console.log(model.id)
        }
        // 每次点击聚焦到点击节点上
        graph.on('node:click', nodeClick);
        graph.on('edge:click', edgeClick);
    }, []);
    // 不会渲染任何东西在canvas上,只把交互逻辑告知canvas
    return null;
};
const Child = () => {
    const [autoPin, setAutoPin] = useState(false);
    const onChange = checked => {
            setAutoPin(checked);
    };
    const data = {
        combos: undefined,
        edges:[
            {id: 'edge0', source:"node0", target:"node1"},
            {id: 'edge1', source:"node0", target:"node2"},
            {id: 'edge2', source:"node0", target:"node3"},
            {id: 'edge3', source:"node0", target:"node4"},
            {id: 'edge4', source:"node0", target:"node5"},
        ],
        nodes:[
            {id: 'node0', label: 'node0', type: 'graphin-circle'},
            {id: 'node1', label: 'node1', type: 'graphin-circle'},
            {id: 'node2', label: 'node2', type: 'graphin-circle'},
            {id: 'node3', label: 'node3', type: 'graphin-circle'},
            {id: 'node4', label: 'node4', type: 'graphin-circle'},
            {id: 'node5', label: 'node5', type: 'graphin-circle'},
            {id: 'node6', label: 'node6', type: 'graphin-circle'},
        ]
    }
    return (
        <div>
            <Graphin data={data} layout={layout}>
                <DragNodeWithForce autoPin={autoPin} />
                <Toolbar>
                    <Toolbar.Item>
                        被拖拽的节点,是否自动固定住
                    </Toolbar.Item>
                    <Toolbar.Item>
                        啊,这一集又没有一格是我呢
                    </Toolbar.Item>
                </Toolbar>

                <ClickBehavior/>
            </Graphin>
        </div>
    );
};
export default Child

20211113114536.gif

了解了基本的click事件其实已经可以解决80%的自定义交互的问题了,因为图交互这种事情基本都是很依赖鼠标点击边或者点的,把这一块做好后自定义事件会很容易,不比操作一个个实例差多少的!