需求:
使用react实现类似如下的可视化画拓扑图的界面
分析
可以看出主要功能点有
1.可视化拖拽组件
2.绘制节点和路径
经查阅,找到了与需求十分接近的组件
GGEditor
这是由阿里的一位大佬写的开源的可视化图编辑器,可以实现绘制节点和路径的功能,支持常用快捷键,如ctrl+c,ctrl+v,delete等,效果如下图:
demo地址
使用
简单地介绍完需要的库后,开始使用。
1.创建一个名为demo的react项目,为了方便,我将使用typescript语言
npx create-react-app demo --typescript
2.安装GGEditor
npm install gg-editor --save
3.为了方便开发,可以使用热更新插件react-refresh,参考此处
4.首先,删除demo/src目录下不必要的文件
5.画流程图
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import GGEditor, {Flow} from 'gg-editor';
//数据
const data = {
nodes: [
{
id: '0',
label: '点1',
x: 55,
y: 55,
},
{
id: '1',
label: '点2',
x: 55,
y: 255,
},
],
edges: [
{
label: '边1',
source: '0',
target: '1',
},
],
};
class App extends React.Component {
render() {
return (
<GGEditor>
<Flow style={{ width: 500, height: 500 }} data={data} />
</GGEditor> );
}
}
ReactDOM.render(<App />, document.getElementById('root'));
6.带命令工具栏
import React from 'react';
import ReactDOM from 'react-dom';
import GGEditor, {Command, ContextMenu, Flow, constants} from 'gg-editor';
import styles from './index.less';
import { Icon } from '@ant-design/compatible';
import {Divider, Tooltip} from 'antd';
import upperFirst from 'lodash/upperFirst';
const { EditorCommand } = constants;
const IconFont = Icon.createFromIconfontCN({
scriptUrl: 'https://at.alicdn.com/t/font_1518433_oa5sw7ezue.js',
});
const FLOW_COMMAND_LIST = [
EditorCommand.Undo,
EditorCommand.Redo,
'|',
EditorCommand.Copy,
EditorCommand.Paste,
EditorCommand.Remove,
'|',
EditorCommand.ZoomIn,
EditorCommand.ZoomOut,
];
const flowData = {
nodes: [
{
id: '0',
label: 'Node',
x: 50,
y: 50,
},
{
id: '1',
label: 'Node',
x: 50,
y: 200,
},
],
edges: [
{
label: 'Label',
source: '0',
sourceAnchor: 1,
target: '1',
targetAnchor: 0,
},
],
};
function App() {
return (
<GGEditor>
<div className={styles.toolbar}>
{FLOW_COMMAND_LIST.map((name, index) => {
if (name === '|') {
return <Divider key={index} type="vertical" />;
}
return (
<Command key={name} name={name} className={styles.command} disabledClassName={styles.commandDisabled}>
<Tooltip title={upperFirst(name)}>
<IconFont type={`icon-${name}`} />
</Tooltip> </Command> );
})}
</div>
<Flow className={styles.graph} data={flowData} />
{/* <Mind className={styles.graph} data={mindData} /> */}
<ContextMenu
renderContent={(item, position, hide) => {
const { x: left, y: top } = position;
return (
<div className={styles.contextMenu} style={{ position: 'absolute', top, left }}>
{[EditorCommand.Undo, EditorCommand.Redo, EditorCommand.PasteHere].map(name => {
return (
<Command key={name} name={name} className={styles.command} disabledClassName={styles.commandDisabled}>
<div onClick={hide}>
<IconFont type={`icon-${name}`} />
<span>{upperFirst(name)}</span>
</div> </Command> );
})}
</div>
);
}}
/>
</GGEditor> );
}
ReactDOM.render(<App />, document.getElementById('root'));
7.节点提供栏
import React from 'react';
import ReactDOM from 'react-dom';
import GGEditor, { Flow, Item, ItemPanel} from 'gg-editor';
import styles from './index.less';
const data = {
nodes: [
{
id: '0',
label: 'Node',
x: 50,
y: 50,
},
{
id: '1',
label: 'Node',
x: 50,
y: 200,
},
],
edges: [
{
label: 'Label',
source: '0',
sourceAnchor: 1,
target: '1',
targetAnchor: 0,
},
],
};
function App() {
return (
<GGEditor>
<ItemPanel className={styles.itemPanel}>
<Item className={styles.item}
model={{
type: 'circle',
size: 50,
label: 'circle',
}}
>
<img src="https://gw.alicdn.com/tfs/TB1IRuSnRr0gK0jSZFnXXbRRXXa-110-112.png"
width="55"
height="56"
draggable={false}
/>
</Item> <Item className={styles.item}
model={{
type: 'rect',
size: [80, 24],
label: 'rect',
}}
>
<img src="https://gw.alicdn.com/tfs/TB1reKOnUT1gK0jSZFrXXcNCXXa-178-76.png"
width="89"
height="38"
draggable={false}
/>
</Item> <Item className={styles.item}
model={{
type: 'ellipse',
size: [100, 50],
label: 'ellipse',
}}
>
<img src="https://gw.alicdn.com/tfs/TB1AvmVnUH1gK0jSZSyXXXtlpXa-216-126.png"
width="108"
height="63"
draggable={false}
/>
</Item> <Item className={styles.item}
model={{
type: 'diamond',
size: [80, 80],
label: 'diamond',
}}
>
<img src="https://gw.alicdn.com/tfs/TB1EB9VnNz1gK0jSZSgXXavwpXa-178-184.png"
width="89"
height="92"
draggable={false}
/>
</Item> <Item className={styles.item}
model={{
type: 'triangle',
size: [30, 30],
label: 'triangle',
}}
>
<img src="https://gw.alicdn.com/tfs/TB12sC2nKH2gK0jSZJnXXaT1FXa-126-156.png"
width="63"
height="78"
draggable={false}
/>
</Item> </ItemPanel> <Flow className={styles.graph} data={data} />
</GGEditor> );
}
ReactDOM.render(<App />, document.getElementById('root'));
然而,但这里就出现问题了,itempanel画出来的节点并没有可以连接的锚点,经过检索,找官网文档,发现一大半已经删除了
github上有人也遇到了和我一样的问题,然而根本没有人回应
直到我在检索到这句话时,一切都明白了
看来官方似乎也把这个项目放弃了,一不小心就踩了一个大坑啊。
总结
这个组件,在官网展示的例子上有非常简洁实用的示例,但在我初步上手后发现文档残缺十分严重,遇到问题完全无法解决,除非读代码,对react新手及其不友好,且antv官方似乎都有放弃的痕迹,非大佬不能使用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。