JTopo踩坑记 – React项目中使用JTopo
最近实习公司一个项目需要绘制电网的拓扑图,大致要求的效果如下:
首先想到的是Echarts和d3,因为这个项目其他图表都是使用的Echarts, 但是在Echarts的官方示例以及 Gallery中并没有找到类似的示例,并且Echarts的自动布局大多为环形布局,不符合项目需求。所以只能寻找其他的node包。然后就找到了JTopo,但是在官网上只有js文件,之后在npm官网找到了node版本的JTopo。需要说明的是,目前所有es6版本的JTopo相关的包都是由JTopo爱好者贡献的。一开始我只关注了JTopo这一个包, 但是在实际的使用中我发现了很多问题,并且很多JTopo官网有的功能这个包没有实现,这也导致我最终放弃了这个包, 而选择了JTopo-in-node。npm安装指令:
npm i jtopo-in-node
JTopo这个包只是封装了一些基本的功能,比如创建node和link,虽然作者说的api和JTopo官网的类似,但是还是有一些差别的,比如官网给一个scene添加node或者link使用scene.add(node),但是在这个包中需要使用scene.name.add(node)。还有很多功能没有实现比如node的alarm以及tip只能在节点顶部绘制,无法显示link的名称;甚至还有一些错误,比如作者Github中给的vue写的示例,node的Tip显示会出错。在使用中我发现当canvas的大小超出了页面的高度,将页面滑动的时候会出现鼠标无法选择节点的情况,只有回到页面加载成功时的位置才能正常使用。
一开始我使用这个包进行开发,并且画出了大概的图
但是新的需求来了,需要平移和缩放以及查找节点。我发现没有继续使用这个包无法实现这个包。因为这个图上需要显示实时的数据,所以每一次有新的数据都会导致页面重绘,并且由于这个包没有提供自动布局,只能自己计算位置,所以新的需求没有办法实现,之后我就找到了jtopo-in-node。
jtopo-in-node的api和JTopo官网几乎一样,可以参考官网的api教程,目前我只使用React实现了官网的一个实例:
代码:
import React, { PureComponent } from 'react'
import JTopo from 'jtopo-in-node';
import {Row, Col, Card} from 'antd';
class DeviceGraph extends React.Component{
state = {
canvasWidth: '300',
position: [],
allSensors: []
};
componentDidMount() {
var canvas = this.refs.test;
canvas.width = window.innerWidth * 0.95;
this.setState({
canvasWidth: window.innerWidth * 0.95
});
}
node = (scene, x, y, img) => {
var node = new JTopo.Node();
node.setImage('http://www.jtopo.com/demo/img/statistics/' + img, true);
node.setLocation(x, y);
node.dragable = false;
node.fontColor = '0,0,0';
scene.add(node);
return node;
};
linkNode = (scene,nodeA, nodeZ, f) => {
var link;
if(f){
link = new JTopo.FoldLink(nodeA, nodeZ, "test");
}else{
link = new JTopo.Link(nodeA, nodeZ);
}
link.direction = 'vertical';
scene.add(link);
return link;
};
hostLink = (scene, nodeA, nodeZ) => {
var link = new JTopo.FlexionalLink(nodeA, nodeZ);
link.shadow = false;
link.offsetGap = 44;
scene.add(link);
return link;
};
render(){
console.log('JTopo', JTopo);
// canvas元素存在之后再进行操作
if(this.refs.test){
var stage = new JTopo.Stage(this.refs.test);
stage.eagleEye.visible = null;
stage.wheelZoom = 0.95;
var scene = new JTopo.Scene(stage);
scene.background = 'http://www.jtopo.com/demo/img/bg.jpg';
var s1 = this.node(scene,305, 43, 'server.png');
s1.alarm = '2 W';
var s2 = this.node(scene,365, 43, 'server.png');
var s3 = this.node(scene,425, 43, 'server.png');
var g1 = this.node(scene,366, 125, 'gather.png');
this.linkNode(scene,s1, g1, true);
this.linkNode(scene,s2, g1, true);
this.linkNode(scene,s3, g1, true);
var w1 = this.node(scene,324, 167, 'wanjet.png');
this.linkNode(scene,g1, w1);
var c1 = this.node(scene,364, 214, 'center.png');
this.linkNode(scene,w1, c1);
var cloud = this.node(scene,344, 259, 'cloud.png');
this.linkNode(scene,c1, cloud);
var c2 = this.node(scene,364, 328, 'center.png');
this.linkNode(scene,cloud, c2);
var w2 = this.node(scene,324, 377, 'wanjet.png');
this.linkNode(scene,c2, w2);
var g2 = this.node(scene,366, 411, 'gather.png');
this.linkNode(scene,w2, g2);
var h1 = this.node(scene,218, 520, 'host.png');
h1.alarm = '';
this.hostLink(scene,g2, h1);
var h2 = this.node(scene,292, 520, 'host.png');
this.hostLink(scene,g2, h2);
var h3 = this.node(scene,366, 520, 'host.png');
h3.alarm = '二级告警';
h3.text = 'h3';
this.hostLink(scene,g2, h3);
var h4 = this.node(scene,447, 520, 'host.png');
this.hostLink(scene,g2, h4);
var h5 = this.node(scene,515, 520, 'host.png');
h5.alarm = '1M';
this.hostLink(scene,g2, h5);
stage.setCenter(515, 520)
setInterval(function(){
if(h3.alarm == '二级告警'){
h3.alarm = null;
h3.text = 'h3'+ Math.random()
}else{
h3.alarm = '二级告警'
}
}, 600);
}
return(
)
}
}
export default DeviceGraph;
没有安装antd的同学可以使用将render中的return换成
在控制台输出JTopo可以看到几乎实现了JTopo官网的所有功能:
目前还没有用到项目当中,等到使用了之后再总结一下使用经验。
最后感谢JTopo原作者以及编写es6版本的爱好者,欢迎大家加入JTopo交流群:171820448 。