简介
GOJS是一个能够让我们很容易的实现基于html5浏览器绘制具有交互性的图形图表的JavaScript框架。
我们需要做的就是创建图形对象、构建数据模型、设置属性、绑定数据模型、使用工具类添加行为即可创建出具有丰富交互性能的各种图表
官网
引入
添加依赖 npm install gojs --save
// 引入gojs
import Vue from 'vue'
import go from 'gojs';
// 全局使用 this.go
Vue.prototype.go = go;
Vue.prototype.flow = go.GraphObject.make; //核心
去除水印
// 在go.js混淆代码中尝试去掉水印:
//a.ax = d[u.Da("7eba17a4ca3b1a8346")][u.Da("78a118b7")](d, u.wl, 4, 4);
a.ax= function(){return true;}
<template>
<div id="mgflowDiv" style="width:1000px; height:500px; background-color: #DAE4E4;"></div>
</template>
import go from 'gojs';
const mgflow = go.GraphObject.make; //核心
const myDiagram = mgflow(go.Diagram, this.$el, {
initialContentAlignment: go.Spot.Center,
// 模型图的中心位置所在坐标
"undoManager.isEnabled": true
// 启用Ctrl-Z撤销和Ctrl-Y重做快捷键
});
最基本的
var myModel = mgflow(go.Model);
myModel.nodeDataArray = [
{ key: "Alpha" },
{ key: "Beta" },
{ key: "Gamma" }
];
//将模型数据绑定到模型图上
myDiagram.model = myModel;
动态连线图
var model = mgflow(go.GraphLinksModel);
model.nodeDataArray =[ { key: "A" }, { key: "B" }, { key: "C" }];
model.linkDataArray =[ { from: "A", to: "B" }, { from: "B", to: "C" }];
myDiagram.model = model;
树形图模型
var model = mgflow(go.TreeModel);
model.nodeDataArray = [
{ key: "1", name: "Don Meow", source: "cat1.png" },
{ key: "2", parent: "1", name: "Demeter", source: "cat2.png" },
{ key: "3", parent: "1", name: "Copricat", source: "cat3.png" },
{ key: "4", parent: "3", name: "Jellylorum", source: "cat4.png" },
{ key: "5", parent: "3", name: "Alonzo", source: "cat5.png" },
{ key: "6", parent: "2", name: "Munkustrap", source: "cat6.png" }
];
myDiagram.model = model;
// 默认TreeLayout是从左到右的,修改为从上到下的代码
myDiagram.layout =mgflow(go.TreeLayout, { angle: 90, layerSpacing: 35 });
// // Shape:Rectangle(矩形)、RoundedRectangle(圆角矩形),Ellipse(椭圆形),Triangle(三角形),Diamond(菱形),Circle(圆形)等
// // TextBlock:文本域(可编辑)
// // Picture:图片
// // Panel:容器来保存其他Node的集合
// myDiagram.nodeTemplate =mgflow(go.Node,mgflow(go.TextBlock,new go.Binding("text","key")));
// myDiagram.nodeTemplate =
// mgflow(go.Node, "Vertical",{ /* 参数 */ },
// // 绑定节点坐标Node.location为Node.data.loc的值 Model对象可以通过Node.data.loc 获取和设置Node.location(修改节点坐标)
// new go.Binding("location", "loc"),
// //Shape上面有个 TextBlock
// mgflow(go.Shape,"RoundedRectangle",{ /* 宽高颜色等等*/ },
// // 绑定 Shape.figure属性为Node.data.fig的值,Model对象可以通过Node.data.fig 获取和设置Shape.figure(修改形状)
// new go.Binding("figure", "fig")),
// mgflow(go.TextBlock,"默认文本",{ /* 字体样式 */ },
// // 绑定TextBlock.text属性为Node.data.key的值,Model对象可以通过Node.data.key获取和设置TextBlock.text(修改文本)
// new go.Binding("text", "key"))
// );
myDiagram.nodeTemplate =
// 布局方式,垂直分布:Vertical,横向布局:Horizontal
mgflow(go.Node, "Horizontal", { background: "#44CCFF" },
mgflow(go.Picture, { margin: 10, width: 50, height: 50, background: "red" },
new go.Binding("source")),
mgflow(go.TextBlock, "Default Text", { margin: 12, stroke: "white", font: "bold 16px sans-serif" },
new go.Binding("text", "name"))
);
<template>
<!-- 渲染图层 -->
<div id="mgflowDiv" style="width:1000px; height:500px; background-color: #DAE4E4;"></div>
</template>
<script>
import go from 'gojs';
const mgflow = go.GraphObject.make; //核心
export default {
name: 'MyFlow',
data() {
return {}
},
methods: {
aaa(data) {}
},
mounted() {
//创建模型图
const myDiagram = mgflow(go.Diagram, this.$el, {
initialContentAlignment: go.Spot.Center,
"undoManager.isEnabled": true
});
// 默认TreeLayout是从左到右的,修改为从上到下的代码
myDiagram.layout =
mgflow(go.TreeLayout, { angle: 90, layerSpacing: 35 });
myDiagram.nodeTemplate =
// 布局方式,垂直分布:Vertical,横向布局:Horizontal
mgflow(go.Node, "Horizontal", { background: "#44CCFF" },
mgflow(go.Picture, { margin: 10, width: 50, height: 50, background: "red" },
new go.Binding("source")),
mgflow(go.TextBlock, "Default Text", { margin: 12, stroke: "white", font: "bold 16px sans-serif" },
new go.Binding("text", "name"))
);
var model = mgflow(go.TreeModel);
model.nodeDataArray = [
{ key: "1", name: "Don Meow", source: "cat1.png" },
{ key: "2", parent: "1", name: "Demeter", source: "cat2.png" },
{ key: "3", parent: "1", name: "Copricat", source: "cat3.png" },
{ key: "4", parent: "3", name: "Jellylorum", source: "cat4.png" },
{ key: "5", parent: "3", name: "Alonzo", source: "cat5.png" },
{ key: "6", parent: "2", name: "Munkustrap", source: "cat6.png" }
];
myDiagram.model = model;
}
};
</script>
<style>
</style>
<template>
<div>
<div style="width:100%; white-space:nowrap;">
<span style="display: inline-block; vertical-align: top; width:100px">
<div id="myPaletteDIV" style="border: solid 1px black; height: 650px"></div>
</span>
<span style="display: inline-block; vertical-align: top; width:80%">
<div id="myDiagramDiv" style="border: solid 1px black; height: 650px"></div>
</span>
</div>
<button id="SaveButton" @click="save()">Save</button>
<button @click="load()">Load</button>
<button @click="makeSVG()">makeSVG</button>
<button class="enlarge">+</button>
<button class="narrow">-</button>
<div id="SVGArea"></div>
<div id="contextMenu">
<ul>
<li id="cut" onclick="cxcommand(event)"><a href="#" target="_self">Cut</a></li>
<li id="copy" onclick="cxcommand(event)"><a href="#" target="_self">Copy</a></li>
<li id="paste" onclick="cxcommand(event)"><a href="#" target="_self">Paste</a></li>
<li id="delete" onclick="cxcommand(event)"><a href="#" target="_self">Delete</a></li>
<li id="color" class="hasSubMenu"><a href="#" target="_self">Color</a>
<ul class="subMenu" id="colorSubMenu">
<li style="background: crimson;" onclick="cxcommand(event, 'color')"><a href="#" target="_self">Red</a></li>
<li style="background: chartreuse;" onclick="cxcommand(event, 'color')"><a href="#" target="_self">Green</a></li>
<li style="background: aquamarine;" onclick="cxcommand(event, 'color')"><a href="#" target="_self">Blue</a></li>
<li style="background: gold;" onclick="cxcommand(event, 'color')"><a href="#" target="_self">Yellow</a></li>
</ul>
</li>
</ul>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import go from 'gojs';
Vue.prototype.go = go;
Vue.prototype.flow = go.GraphObject.make; //核心
var lightText = 'whitesmoke';
export default {
name: 'MyFlow',
props: {},
data() {
return {
myPalette: Object,
myDiagram: Object,
myContextMenu: Object,
data: {
"class": "go.GraphLinksModel",
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{ "key": -1, "category": "Start", "loc": "175 0", "text": "Start" },
{ "key": 0, "loc": "175 100", "text": "if/else", "figure": "Diamond" },
{ "key": 1, "loc": "10 200", "text": "text1" },
{ "key": 2, "loc": "175 200", "text": "text2" },
{ "key": 3, "loc": "357 200", "text": "text3" },
{ "key": 4, "loc": "175 400", "text": "text4" },
{ "key": -2, "category": "End", "loc": "175 500", "text": "End!" }
],
"linkDataArray": [
{ "from": 3, "to": 4, "fromPort": "B", "toPort": "T" },
{ "from": 5, "to": 4, "fromPort": "B", "toPort": "T" },
{ "from": -1, "to": -3, "fromPort": "B", "toPort": "T" },
{ "from": -3, "to": 0, "fromPort": "L", "toPort": "T", "visible": true, "text": "选择1" },
{ "from": -3, "to": 5, "fromPort": "R", "toPort": "T", "visible": true, "text": "选择3" },
{ "from": -3, "to": 3, "fromPort": "B", "toPort": "T", "visible": true, "text": "选择2" },
{ "from": 4, "to": -2, "fromPort": "B", "toPort": "T" },
{ "from": -1, "to": 0, "fromPort": "B", "toPort": "T" },
{ "from": 0, "to": 1, "fromPort": "L", "toPort": "T", "visible": true, "text": "选择1" },
{ "from": 0, "to": 3, "fromPort": "R", "toPort": "T", "visible": true, "text": "选择3" },
{ "from": 0, "to": 2, "fromPort": "B", "toPort": "T", "visible": true, "text": "选择2" },
{ "from": 1, "to": 4, "fromPort": "B", "toPort": "T", },
{ "from": 2, "to": 4, "fromPort": "B", "toPort": "T" }
]
}
}
},
methods: {
initDiagram() {
this.myDiagram = this.flow(this.go.Diagram, 'myDiagramDiv', {
initialContentAlignment: this.go.Spot.Center, // 模型图的中心位置所在坐标
allowDrop: true, //接受从 Palette 中删除的内容
"LinkDrawn": this.showLinkLabel, // this DiagramEvent listener is defined below
"LinkRelinked": this.showLinkLabel,
scrollsPageOnFocus: false,
"undoManager.isEnabled": true // 启用Ctrl-Z撤销和Ctrl-Y重做快捷键
});
},
initPalette() {
this.myPalette = this.flow(this.go.Palette, 'myPaletteDIV', {
scrollsPageOnFocus: false,
// 分享 myDiagram 的模板
nodeTemplateMap: this.myDiagram.nodeTemplateMap,
// 指定 myPalette 显示的内容
model: new this.go.GraphLinksModel([
{ category: "Start", text: "Start" },
{ text: "Step" },
{ text: "???", figure: "Diamond" },
{ category: "End", text: "End" },
{ category: "Comment", text: "Comment" }
])
});
},
makePort(name, spot, output, input) {
// 指针在上面时的显示
return this.flow(this.go.Shape, "Circle", {
fill: "transparent",
stroke: null, // "white"
desiredSize: new this.go.Size(8, 8),
alignment: spot,
alignmentFocus: spot,
portId: name,
fromSpot: spot,
toSpot: spot,
fromLinkable: output,
toLinkable: input,
cursor: "pointer"
});
},
nodeStyle() {
let _self = this;
return [
// 绑定节点坐标Node.location为Node.data.loc的值,Model对象可以通过Node.data.loc 获取和设置Node.location(修改节点坐标)
new this.go.Binding("location", "loc", _self.go.Point.parse).makeTwoWay(_self.go.Point.stringify), {
locationSpot: _self.go.Spot.Center,
//isShadowed: true,
//shadowColor: "#888",
mouseEnter: function(e, obj) { _self.showPorts(obj.part, true); },
mouseLeave: function(e, obj) { _self.showPorts(obj.part, false); }
}
];
},
showLinkLabel(e) {
var label = e.subject.findObject("LABEL");
if (label !== null) label.visible = (e.subject.fromNode.data.figure === "Diamond");
},
// 节点Node
addNodeTplDefault() {
this.myDiagram.nodeTemplateMap.add("", // 默认类型
this.flow(this.go.Node, "Spot", { contextMenu: this.myContextMenu }, this.nodeStyle(),
this.flow(this.go.Panel, "Auto",
this.flow(this.go.Shape, "Rectangle", { fill: "#00A9C9", stroke: null },
new this.go.Binding("figure", "figure")), this.flow(this.go.TextBlock, {
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: 'whitesmoke',
margin: 8,
maxSize: new this.go.Size(300, NaN),
wrap: this.go.TextBlock.WrapFit,
editable: false // 节点是否可编辑
},
new this.go.Binding("text").makeTwoWay())
),
// four named ports, one on each side:
this.makePort("T", this.go.Spot.Top, false, true),
this.makePort("L", this.go.Spot.Left, true, true),
this.makePort("R", this.go.Spot.Right, true, true),
this.makePort("B", this.go.Spot.Bottom, true, false)
));
},
addNodeTplStart() {
this.myDiagram.nodeTemplateMap.add("Start",
this.flow(this.go.Node, "Spot", this.nodeStyle(),
this.flow(this.go.Panel, "Auto",
this.flow(this.go.Shape, "Circle", { minSize: new this.go.Size(40, 40), fill: "#79C900", stroke: null }),
this.flow(this.go.TextBlock, "Start", { font: "bold 11pt Helvetica, Arial, sans-serif", stroke: lightText },
new this.go.Binding("text"))
),
// three named ports, one on each side except the top, all output only:
this.makePort("L", this.go.Spot.Left, true, false),
this.makePort("R", this.go.Spot.Right, true, false),
this.makePort("B", this.go.Spot.Bottom, true, false)
));
},
addNodeTplEnd() {
this.myDiagram.nodeTemplateMap.add("End",
this.flow(this.go.Node, "Spot", this.nodeStyle(),
this.flow(this.go.Panel, "Auto",
this.flow(this.go.Shape, "Circle", { minSize: new this.go.Size(40, 40), fill: "#DC3C00", stroke: null }),
this.flow(this.go.TextBlock, "End", { font: "bold 11pt Helvetica, Arial, sans-serif", stroke: lightText },
new this.go.Binding("text"))
),
// three named ports, one on each side except the bottom, all input only:
this.makePort("T", this.go.Spot.Top, false, true),
this.makePort("L", this.go.Spot.Left, false, true),
this.makePort("R", this.go.Spot.Right, false, true)
));
},
addNodeTplComment() {
this.myDiagram.nodeTemplateMap.add("Comment",
this.flow(this.go.Node, "Auto", this.nodeStyle(),
this.flow(this.go.Shape, "File", { fill: "#EFFAB4", stroke: null }),
this.flow(this.go.TextBlock, {
margin: 5,
maxSize: new this.go.Size(400, NaN),
wrap: this.go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 12pt Helvetica, Arial, sans-serif",
stroke: '#454545'
},
new this.go.Binding("text").makeTwoWay())
));
},
addNodelinkTpl() {
this.myDiagram.linkTemplate =
this.flow(this.go.Link, {
routing: this.go.Link.AvoidsNodes,
curve: this.go.Link.JumpOver,
corner: 5,
toShortLength: 4,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true,
//selectable: false,
mouseEnter: function(e, link) { link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"; },
mouseLeave: function(e, link) { link.findObject("HIGHLIGHT").stroke = "transparent"; }
},
new this.go.Binding("points").makeTwoWay(),
this.flow(this.go.Shape, { isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" }),
this.flow(this.go.Shape, { isPanelMain: true, stroke: "gray", strokeWidth: 2 }),
this.flow(this.go.Shape, { toArrow: "standard", stroke: null, fill: "gray" }),
this.flow(this.go.Panel, "Auto", { visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5 },
new this.go.Binding("visible", "visible").makeTwoWay(),
this.flow(this.go.Shape, "RoundedRectangle", { fill: "#F8F8F8", stroke: null }),
this.flow(this.go.TextBlock, "Yes", {
textAlign: "center",
font: "10pt helvetica, arial, sans-serif",
stroke: "#333333",
editable: true
},
new this.go.Binding("text").makeTwoWay())
)
);
},
showPorts(node, show) {
var diagram = node.diagram;
if (!diagram || diagram.isReadOnly || !diagram.allowLink) return;
node.ports.each(function(port) {
port.stroke = (show ? "white" : null);
});
},
save() {
// document.getElementById("mySavedModel").value = this.myDiagram.model.toJson();
this.data = this.myDiagram.model.toJson();
console.log(this.data);
this.myDiagram.isModified = false;
},
load() {
// this.myDiagram.model = this.go.Model.fromJson(document.getElementById("mySavedModel").value);
this.myDiagram.model = this.go.Model.fromJson(this.data);
},
makeSVG() {
var svg = this.myDiagram.makeSvg({
scale: 0.5
});
svg.style.border = "1px solid black";
var obj = document.getElementById("SVGArea");
obj.appendChild(svg);
if (obj.children.length > 0) {
obj.replaceChild(svg, obj.children[0]);
}
}
},
mounted() {
let _self = this;
this.initDiagram();
// 修改后 自动保存
this.myDiagram.addDiagramListener("Modified", function(e) {
_self.save();
});
// // 节点单击事件
// this.myDiagram.addDiagramListener("ObjectSingleClicked", function(e) {
// console.log('单击事件',e.subject.part.data);
// alert(e.subject.part.data.key) ;
// });
// 节点双击事件
this.myDiagram.addDiagramListener("ObjectDoubleClicked", function(ev) {
alert(ev.subject);
console.log('双击事件', ev.subject);
console.log('双击事件', ev.subject.ie);
});
// // 节点右击事件
// this.myDiagram.addDiagramListener("ObjectContextClicked", function (ev) {
// // ev.preventDefault();
// return false;
// });
var cxElement = document.getElementById("contextMenu");
function showContextMenu(obj, diagram, tool) {
cxElement.style.display = "block";
var mousePt = diagram.lastInput.viewPoint;
cxElement.style.left = (mousePt.x + 200) + "px";
cxElement.style.top = mousePt.y + "px";
}
this.myContextMenu = this.flow(this.go.HTMLInfo, {
show: showContextMenu,
mainElement: cxElement
});
cxElement.addEventListener("contextmenu", function(e) {
e.preventDefault();
return false;
}, false);
// this.myDiagram.contextMenu = this.myContextMenu;
// 背景点击事件
this.myDiagram.addDiagramListener("BackgroundSingleClicked", function(e) {
console.log(e);
// alert(e.subject.part.data.key) ;
});
//点击放大缩小画布
this.myDiagram.scale = 0.7;
this.myDiagram.minScale = 0.4;
this.myDiagram.maxScale = 1.4;
$(".enlarge").click(function() {
_self.myDiagram.scale += 0.1;
})
$(".narrow").click(function() {
_self.myDiagram.scale -= 0.1;
})
this.addNodeTplDefault();
this.addNodeTplStart();
this.addNodeTplEnd();
this.addNodeTplComment();
this.addNodelinkTpl();
this.initPalette();
this.load();
}
};
</script>
<style type="text/css">
/* CSS for the traditional context menu */
#contextMenu {
z-index: 300;
position: absolute;
left: 5px;
border: 1px solid #444;
background-color: #F5F5F5;
display: none;
box-shadow: 0 0 10px rgba( 0, 0, 0, .4);
font-size: 12px;
font-family: sans-serif;
font-weight: bold;
}
#contextMenu ul {
list-style: none;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
#contextMenu li a {
position: relative;
min-width: 60px;
color: #444;
display: inline-block;
padding: 6px;
text-decoration: none;
cursor: pointer;
}
#contextMenu li:hover {
background: #CEDFF2;
color: #EEE;
}
#contextMenu li ul li {
display: none;
}
#contextMenu li ul li a {
position: relative;
min-width: 60px;
padding: 6px;
text-decoration: none;
cursor: pointer;
}
#contextMenu li:hover ul li {
display: block;
margin-left: 0px;
margin-top: 0px;
}
</style>