JointJS是一个开源的、基于JavaScript的图表库,可以用来创建静态图表、完全可交互的图表、 WEB在线流程图、应用程序
jointJS是一个基于svg的图形化工具库,在画布上画出支持拖动的svg图形,而且可以导出JSON,也能通过JSON配置导入直接生成图形。 可以基于jointJS开发出流程图、UML图以及图表等。由于jointJS是基于svg的,因此对svg有一定的了解会对jointJS的理解和使用有较大帮助。 由于jointJS是基于backbone的,因此有view(视图)和model(模型)的概念。 使用jointJS需要引入jQuery、backbone、lodash以及jointJS的包,可以通过script标签引入,也可以通过npm安装。
因为业务需求,主要需要满足:
1、初始化流程图
2、两种节点,一种“一进一出”,另外一种“一进多出”(未完美实现)
3、不同状态显示不同颜色连线(暂未实现)
4、可编辑,节点跟Link需要自定义
一开始,把官网上的初级、中级教程过一遍,大致了解他的构建流程(比如画布构建,Model、Graph、Link等概念,还有工具属性以及触发方法等),挑战才刚刚开始...
比较坑的一个点是:Rappid(商业收费,好贵)是高度集成了Joint.js的很多常见的业务场景。但是,Joint.js的demo,列举的例子,隐含了Rappid的混淆引用,就是还是基于Rappid进行展示,但是工具类外部属性(暂时发现)是经过混淆的(可能是为了引导大家去使用收费的Rappid)。文档也不够清晰,可能是我还没熟悉这种模式(套路)
<template>
<div>
<div id="toolbar">
<button class="btn add-question" @click='addAudioNode'>Add Audio-Node</button>
<button class="btn add-answer" @click='addVideoNode'>Add Video-Node</button>
<button class="btn preview-dialog">Preview Dialog</button>
<button class="btn code-snippet">Code Snippet</button>
<button class="btn clear">Clear Canvas</button>
<button class="btn load-example">Load Example</button>
</div>
<div id="myholder" @click="click_joint"></div>
</div>
</template>
<script>
require('../assets/css/toolbar.css')
import joint from 'jointjs'
import $ from 'jquery'
export default {
name: 'App',
data: function () {
return {
active: true,
graph:null,
rectAudio:null,
rectVideo:null,
}
},
mounted: function () {
this.init()
},
methods: {
init(){
// 先创建joint graph 对象
var graph = new joint.dia.Graph;
this.graph = graph;
//设定画布基本信息
var paper = new joint.dia.Paper({
el: document.getElementById('myholder'),
width: 900,
height: 700,
model: graph,
gridSize:1,
//默认link样式
defaultLink: function(elementView, magnet) {
return new joint.shapes.standard.Link;
},
//限制画布范围内
restrictTranslate: true,
});
//连接节点
var connect = function(source, sourcePort, target, targetPort) {
var link = new joint.shapes.standard.Link({
source: {
id: source.id,
port: sourcePort
},
target: {
id: target.id,
port: targetPort,
},
});
//link工具类-编辑
var verticesTool = new joint.linkTools.Vertices();
var segmentsTool = new joint.linkTools.Segments();
var sourceArrowheadTool = new joint.linkTools.SourceArrowhead();
var targetArrowheadTool = new joint.linkTools.TargetArrowhead();
var sourceAnchorTool = new joint.linkTools.SourceAnchor();
var targetAnchorTool = new joint.linkTools.TargetAnchor();
var boundaryTool = new joint.linkTools.Boundary();
var toolsView = new joint.dia.ToolsView({
init:[],
tools: [
verticesTool, segmentsTool,
sourceArrowheadTool, targetArrowheadTool,
sourceAnchorTool, targetAnchorTool,
boundaryTool,
]
});
link.addTo(graph).parent();
var linkView = link.findView(paper);
//link添加可编辑
linkView.addTools(toolsView);
linkView.hideTools();
};
//音频节点
var rect = new joint.shapes.devs.Model({
position: { x: 100, y: 30 },
size: { width: 100, height: 40 },
inPorts: ['in'],
outPorts: ['out'],
ports: {
groups: {
'in': {
attrs: {
'.port-body': {
fill: '#16A085',
magnet: 'passive',
refDx:'-5',
r:'5'
},
}
},
'out': {
attrs: {
'.port-body': {
fill: '#E74C3C',
refDx:'5',
r:'5'
}
}
}
}
},
attrs: {
'.label': { text: 'voice', 'ref-x': .5, 'ref-y': .2, },
rect: { fill: '#2ECC71' }
}
});
//视频节点
var rect_video = new joint.shapes.devs.Model({
position: { x: 200, y: 100 },
size: { width: 100, height: 40 },
inPorts: ['in'],
outPorts: ['out'],
ports: {
groups: {
'in': {
attrs: {
'.port-body': {
fill: '#16A085',
magnet: 'passive',
refDx:'-5',
r:'5'
},
}
},
'out': {
attrs: {
'.port-body': {
fill: '#E74C3C',
refDx:'5',
r:'5'
}
}
}
}
},
attrs: {
'.label': { text: 'video', 'ref-x': .5, 'ref-y': .2, },
rect: { fill: '#FFF' }
}
});
this.rectAudio = rect;
this.rectVideo = rect_video;
rect.addTo(graph);
rect_video.addTo(graph);
//克隆一个节点
let rect2 = rect.clone();
rect2.translate(300,0);
rect2.attr('label/text','node2');
rect2.addTo(graph);
//克隆一个节点
let rect3 = rect.clone();
rect3.translate(500,0);
rect3.attr('label/text','node2');
rect3.addTo(graph);
//链接
graph.addCells([rect, rect2, rect3,]);
connect(rect, 'in', rect2, 'out');
connect(rect2, 'in', rect3, 'out');
//改变element postion
//建议加上防抖,以实现实时保存位置
graph.on('change:position', function(cell) {
var center = cell.getBBox().center();
var label = center.toString();
cell.attr('label/text', label);
});
//双击element事件
paper.on('element:pointerdblclick', function(cellView) {
//保存数据
console.log('data',graph.toJSON())
});
paper.on('link:mouseenter', function(linkView) {
console.log('111')
linkView.showTools();
});
paper.on('link:mouseleave', function(linkView) {
console.log('222')
linkView.hideTools();
});
},
//克隆一个音频节点
addAudioNode(){
let rect3 = this.rectAudio.clone();
rect3.translate(Math.random()*100+200,0);
rect3.attr('label/text',Math.random()*100+200);
rect3.addTo(this.graph);
},
//克隆一个视频节点
addVideoNode(){
let rect3 = this.rectVideo.clone();
rect3.translate(Math.random()*100+50,0);
rect3.attr('label/text',Math.random()*100+50);
rect3.addTo(this.graph);
},
click_joint(){
console.log('click_joint');
},
},
}
</script>
<style>
#myholder{
width: 900px;
height: 700px;
margin: 0 auto;
margin-top: 25px;
border: 1px solid #d3d3d3;
}
</style>
迁移回本地环境,还没配置好,实现图稍后上传...如有问题可以跟我说吼,谢谢!恳请各位大大指正