JS組件系列之Gojs組件 前端圖形化插件之利器
前言:之前分享過(guò)兩篇關(guān)于流程畫(huà)圖的前端組件,使用的jsPlumb。這個(gè)組件本身還不錯(cuò),使用方便、入門簡(jiǎn)單、輕量級(jí),但是使用一段時(shí)間下來(lái),發(fā)現(xiàn)一些弊病,比如組件不太穩(wěn)定,初始進(jìn)入頁(yè)面的時(shí)候連線的樣式有時(shí)會(huì)亂掉,刷新頁(yè)面之后才能恢復(fù)正常,而且連線樣式比較單一,容易讓人產(chǎn)生視覺(jué)疲勞,加之最近公司在大力推行所謂的“工業(yè)4.0”,除了對(duì)自動(dòng)化控制要求的提高之外,對(duì)這種圖形化界面的要求也隨之提高,所以單純的jsPlumb組件效果已經(jīng)不能滿足日益發(fā)展的公司業(yè)務(wù)?;谝陨戏N種,最終找到了Gojs組件,它效果強(qiáng)大、api豐富,唯一的不足就是這個(gè)組件是一個(gè)收費(fèi)組件,可是在天朝,噓...這是個(gè)不能說(shuō)的秘密!
本文原創(chuàng)地址:http://www.cnblogs.com/landeanfen/p/7910530.html
一、組件效果預(yù)覽
先來(lái)兩個(gè)炫酷點(diǎn)的效果



就最下面兩個(gè)效果而言,就是jsPlumb無(wú)法實(shí)現(xiàn)的,可是這種效果在MES系統(tǒng)里面是很吸引人的,尤其是一些流程性的業(yè)務(wù),用這種效果實(shí)現(xiàn)讓可以一眼就感覺(jué)高大上了。并且咋一眼看上去,你根本都不相信這是一個(gè)web頁(yè)面的效果。
其他效果示例


可折疊的樹(shù)

這是圖片嗎?

竟然還可以生成圖表!



想搶visio的飯碗嗎?



更多示例可查看官網(wǎng)
二、初次接觸
老規(guī)矩,還是先來(lái)個(gè)入門教程。
源碼下載:https://github.com/NorthwoodsSoftware/GoJS
api詳情:https://gojs.net/latest/api/index.html
示例地址:https://gojs.net/latest/samples/index.html
1、Gojs簡(jiǎn)介
GoJS是一個(gè)功能豐富的JS庫(kù),在Web瀏覽器和平臺(tái)上可實(shí)現(xiàn)自定義交互圖和復(fù)雜的可視化效果,它用自定義模板和布局組件簡(jiǎn)化了節(jié)點(diǎn)、鏈接和分組等復(fù)雜的JS圖表,給用戶交互提供了許多先進(jìn)的功能,如拖拽、復(fù)制、粘貼、文本編輯、工具提示、上下文菜單、自動(dòng)布局、模板、數(shù)據(jù)綁定和模型、事務(wù)狀態(tài)和撤銷管理、調(diào)色板、概述、事件處理程序、命令和自定義操作的擴(kuò)展工具系統(tǒng)。無(wú)需切換服務(wù)器和插件,GoJS就能實(shí)現(xiàn)用戶互動(dòng)并在瀏覽器中完全運(yùn)行,呈現(xiàn)HTML5 Canvas元素或SVG,也不用服務(wù)器端請(qǐng)求。 GoJS不依賴于任何JS庫(kù)或框架(例如bootstrap、jquery等),可與任何HTML或JS框架配合工作,甚至可以不用框架。
2、使用入門
(1)文件引用
可以用cdn上面的最新版本,也可以引用本地down下來(lái)的文件。如果是開(kāi)發(fā),可以引用debug版本的js,正式運(yùn)行的時(shí)候引用正式的js,這個(gè)無(wú)需多講。
(2)創(chuàng)建畫(huà)布
隨便定義一個(gè)html元素,作為我們的畫(huà)布
然后使用gojs的api初始化畫(huà)布
//創(chuàng)建畫(huà)布
var objGo = go.GraphObject.make;
var myDiagram = objGo(go.Diagram, "myDiagramDiv",
{
//模型圖的中心位置所在坐標(biāo)
initialContentAlignment: go.Spot.Center,
//允許用戶操作圖表的時(shí)候使用Ctrl-Z撤銷和Ctrl-Y重做快捷鍵
"undoManager.isEnabled": true,
//不運(yùn)行用戶改變圖表的規(guī)模
allowZoom: false,
//畫(huà)布上面是否出現(xiàn)網(wǎng)格
"grid.visible": true,
//允許在畫(huà)布上面雙擊的時(shí)候創(chuàng)建節(jié)點(diǎn)
"clickCreatingTool.archetypeNodeData": { text: "Node" },
//允許使用ctrl+c、ctrl+v復(fù)制粘貼
"commandHandler.copiesTree": true,
//允許使用delete鍵刪除節(jié)點(diǎn)
"commandHandler.deletesTree": true,
// dragging for both move and copy
"draggingTool.dragsTree": true,
});
官方示例用的$符號(hào)作為變量,博主覺(jué)得$符號(hào)太敏感,還是換個(gè)名字吧~以上幾個(gè)參數(shù)都是博主摘選的,更多初始化畫(huà)布的參數(shù)請(qǐng)參考官方api下圖:

(3)創(chuàng)建模型數(shù)據(jù)(Model)
接著上面的代碼,我們?cè)黾尤缦聨仔?/p>
var myModel = objGo(go.Model);//創(chuàng)建Model對(duì)象
// model中的數(shù)據(jù)每一個(gè)js對(duì)象都代表著一個(gè)相應(yīng)的模型圖中的元素
myModel.nodeDataArray = [
{ key: "工廠" },
{ key: "車間" },
{ key: "工人" },
{ key: "崗位" },
];
myDiagram.model = myModel; //將模型數(shù)據(jù)綁定到畫(huà)布圖上
效果預(yù)覽

(4)創(chuàng)建節(jié)點(diǎn)(Node)
上面有了畫(huà)布和節(jié)點(diǎn)數(shù)據(jù),只是有了一個(gè)雛形,但是還沒(méi)有任何的圖形化效果。我們加入一些效果試試
在gojs里面給我們提供了幾種模型節(jié)點(diǎn)的可選項(xiàng):
Shape:形狀——Rectangle(矩形)、RoundedRectangle(圓角矩形),Ellipse(橢圓形),Triangle(三角形),Diamond(菱形),Circle(圓形)等
TextBlock:文本域(可編輯)
Picture:圖片
Panel:容器來(lái)保存其他Node的集合
默認(rèn)的節(jié)點(diǎn)模型代碼只是由一個(gè)TextBlock組件構(gòu)建成
我們?cè)黾尤缦乱欢未a
// 定義一個(gè)簡(jiǎn)單的節(jié)點(diǎn)模板
myDiagram.nodeTemplate =
objGo(go.Node, "Horizontal",//橫向布局的面板
// 節(jié)點(diǎn)淡藍(lán)色背景
{ background: "#44CCFF" },
objGo(go.Shape,
"RoundedRectangle", //定義形狀,這是圓角矩形
{ /* Shape的參數(shù)。寬高顏色等等*/figure: "Club", width: 40, height: 60, margin: 4, fill: 'red' },
// 綁定 Shape.figure屬性為Node.data.fig的值,Model對(duì)象可以通過(guò)Node.data.fig 獲取和設(shè)置Shape.figure(修改形狀)
new go.Binding("figure", "fig"), new go.Binding('fill', 'fill2')),
objGo(go.TextBlock,
"Default Text", // 默認(rèn)文本
// 設(shè)置字體大小顏色以及邊距
{ margin: 12, stroke: "white", font: "bold 16px sans-serif" },
//綁定TextBlock.text 屬性為Node.data.name的值,Model對(duì)象可以通過(guò)Node.data.name獲取和設(shè)置TextBlock.text
new go.Binding("text", "name"))
);
var myModel = objGo(go.Model);//創(chuàng)建Model對(duì)象
// model中的數(shù)據(jù)每一個(gè)js對(duì)象都代表著一個(gè)相應(yīng)的模型圖中的元素
myModel.nodeDataArray = [
{ name: "工廠", fig: 'YinYang', fill2: 'blue' },
{ name: "車間", fig: 'Peace', fill2: 'red' },
{ name: "工人", fig: 'NotAllowed', fill2: 'green' },
{ name: "崗位", fig: 'Fragile', fill2: 'yellow' },
];
myDiagram.model = myModel; //將模型數(shù)據(jù)綁定到畫(huà)布圖上
代碼釋疑:以上我們給畫(huà)布對(duì)象定義了兩種節(jié)點(diǎn)模板,一種是文本節(jié)點(diǎn),另一種是形狀節(jié)點(diǎn)(Node)。在形狀節(jié)點(diǎn)中,我們定義了數(shù)據(jù)模型的通用節(jié)點(diǎn)樣式,就是這一段代碼{ /* Shape的參數(shù)。寬高顏色等等*/figure: "Club", width: 40, height: 60, margin: 4, fill: 'red' },然后通過(guò)new go.Binding("figure", "fig")方法將模板里面的屬性映射到數(shù)據(jù)實(shí)例中,比如這里模板里面的figure屬性定義的是Club,如果在我們的數(shù)據(jù)里面定義fig屬性,那么它就會(huì)覆蓋模板里面的figure的默認(rèn)值。同樣,fill和fill2也是通過(guò)同樣的原理去區(qū)別模板中的樣式和實(shí)例中的實(shí)際樣式的!
注:更多figure屬性的取值詳見(jiàn)這里
效果如下

由此可見(jiàn)我們數(shù)據(jù)里面的屬性會(huì)覆蓋模板的原始屬性,如果是新增的節(jié)點(diǎn),由于沒(méi)有自定義數(shù)據(jù)屬性,所以呈現(xiàn)到界面上面的時(shí)候就是模板里面的原生樣式!
(5)節(jié)點(diǎn)連線
有了上面的基礎(chǔ),我們可以在畫(huà)布上面畫(huà)出我們想要的圖形效果了,可是還沒(méi)有連線。我們知道連線是建立在節(jié)點(diǎn)模型的上面的,于是乎我們的Model又分為了以下三種類型:
Model:最基本的(不帶連線,如上面的例子)
GraphLinksModel :高級(jí)點(diǎn)的動(dòng)態(tài)連線圖
TreeModel:樹(shù)形圖的模型(從例子看好像用的不多)
GraphLinksModel中為model.nodeDataArray提供model.linkDataArray為node節(jié)點(diǎn)連線保存數(shù)據(jù)模型信息,其實(shí)也是的一個(gè)JSON數(shù)組對(duì)象,每個(gè)線條都有兩個(gè)屬性 “to” 和 “from” 即Node節(jié)點(diǎn)的“key”值,兩個(gè)屬性代表兩個(gè)key表示兩個(gè)節(jié)點(diǎn)間的連線。
我們上面已經(jīng)寫(xiě)過(guò)最基本的Model的例子了,我們?cè)賮?lái)個(gè)帶連線的Model的示例
var myModel = objGo(go.GraphLinksModel);
myModel.nodeDataArray =
[
{ key: "aaa" ,name: "工廠" },
{ key: "bbb" ,name: "車間"},
{ key: "ccc" ,name: "車間" }
];
myModel.linkDataArray =
[
{ from: "aaa", to: "bbb" },
{ from: "bbb", to: "ccc" }
];
myDiagram.model = myModel;
效果如下

學(xué)習(xí)了Model、GraphLinksModel,還剩下一種TreeModel樹(shù)節(jié)點(diǎn)的模型,這個(gè)博主不打算做詳細(xì)介紹,有興趣可以直接查看官網(wǎng)。
三、綜合效果
關(guān)于綜合效果,博主不打算將gojs的api逐個(gè)翻個(gè)遍了,這樣太耗時(shí)間,傷不起,只是將官方示例中的部分源碼截取出來(lái)供大家參考。有需要的再細(xì)究!
1、自定義流程的使用
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Draggable Link</title>
<meta name="description" content="Drag a link to reconnect it. Nodes have custom Adornments for selection, resizing, and reshaping." />
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="../../gojs/go-debug.js"></script>
<script id="code">
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var objGo = go.GraphObject.make; // for conciseness in defining templates
myDiagram =
objGo(go.Diagram, "myDiagramDiv", // must name or refer to the DIV HTML element
{
grid: objGo(go.Panel, "Grid",
objGo(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
objGo(go.Shape, "LineH", { stroke: "gray", strokeWidth: 0.5, interval: 10 }),
objGo(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }),
objGo(go.Shape, "LineV", { stroke: "gray", strokeWidth: 0.5, interval: 10 })
),
allowDrop: true, // must be true to accept drops from the Palette
"draggingTool.dragsLink": true,
"draggingTool.isGridSnapEnabled": true,
"linkingTool.isUnconnectedLinkValid": true,
"linkingTool.portGravity": 20,
"relinkingTool.isUnconnectedLinkValid": true,
"relinkingTool.portGravity": 20,
"relinkingTool.fromHandleArchetype":
objGo(go.Shape, "Diamond", { segmentIndex: 0, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "tomato", stroke: "darkred" }),
"relinkingTool.toHandleArchetype":
objGo(go.Shape, "Diamond", { segmentIndex: -1, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "darkred", stroke: "tomato" }),
"linkReshapingTool.handleArchetype":
objGo(go.Shape, "Diamond", { desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue" }),
rotatingTool: objGo(TopRotatingTool), // defined below
"rotatingTool.snapAngleMultiple": 15,
"rotatingTool.snapAngleEpsilon": 15,
"undoManager.isEnabled": true
});
// when the document is modified, add a "*" to the title and enable the "Save" button
myDiagram.addDiagramListener("Modified", function(e) {
var button = document.getElementById("SaveButton");
if (button) button.disabled = !myDiagram.isModified;
var idx = document.title.indexOf("*");
if (myDiagram.isModified) {
if (idx < 0) document.title += "*";
} else {
if (idx >= 0) document.title = document.title.substr(0, idx);
}
});
// Define a function for creating a "port" that is normally transparent.
// The "name" is used as the GraphObject.portId, the "spot" is used to control how links connect
// and where the port is positioned on the node, and the boolean "output" and "input" arguments
// control whether the user can draw links from or to the port.
function makePort(name, spot, output, input) {
// the port is basically just a small transparent square
return objGo(go.Shape, "Circle",
{
fill: null, // not seen, by default; set to a translucent gray by showSmallPorts, defined below
stroke: null,
desiredSize: new go.Size(7, 7),
alignment: spot, // align the port on the main Shape
alignmentFocus: spot, // just inside the Shape
portId: name, // declare this object to be a "port"
fromSpot: spot, toSpot: spot, // declare where links may connect at this port
fromLinkable: output, toLinkable: input, // declare whether the user may draw links to/from here
cursor: "pointer" // show a different cursor to indicate potential link point
});
}
var nodeSelectionAdornmentTemplate =
objGo(go.Adornment, "Auto",
objGo(go.Shape, { fill: null, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] }),
objGo(go.Placeholder)
);
var nodeResizeAdornmentTemplate =
objGo(go.Adornment, "Spot",
{ locationSpot: go.Spot.Right },
objGo(go.Placeholder),
objGo(go.Shape, { alignment: go.Spot.TopLeft, cursor: "nw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.Top, cursor: "n-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.TopRight, cursor: "ne-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.Left, cursor: "w-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.Right, cursor: "e-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.BottomLeft, cursor: "se-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.Bottom, cursor: "s-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { alignment: go.Spot.BottomRight, cursor: "sw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" })
);
var nodeRotateAdornmentTemplate =
objGo(go.Adornment,
{ locationSpot: go.Spot.Center, locationObjectName: "CIRCLE" },
objGo(go.Shape, "Circle", { name: "CIRCLE", cursor: "pointer", desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue" }),
objGo(go.Shape, { geometryString: "M3.5 7 L3.5 30", isGeometryPositioned: true, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] })
);
myDiagram.nodeTemplate =
objGo(go.Node, "Spot",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
{ selectable: true, selectionAdornmentTemplate: nodeSelectionAdornmentTemplate },
{ resizable: true, resizeObjectName: "PANEL", resizeAdornmentTemplate: nodeResizeAdornmentTemplate },
{ rotatable: true, rotateAdornmentTemplate: nodeRotateAdornmentTemplate },
new go.Binding("angle").makeTwoWay(),
// the main object is a Panel that surrounds a TextBlock with a Shape
objGo(go.Panel, "Auto",
{ name: "PANEL" },
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
objGo(go.Shape, "Rectangle", // default figure
{
portId: "", // the default port: if no spot on link data, use closest side
fromLinkable: true, toLinkable: true, cursor: "pointer",
fill: "white", // default color
strokeWidth: 2
},
new go.Binding("figure"),
new go.Binding("fill")),
objGo(go.TextBlock,
{
font: "bold 11pt Helvetica, Arial, sans-serif",
margin: 8,
maxSize: new go.Size(160, NaN),
wrap: go.TextBlock.WrapFit,
editable: true
},
new go.Binding("text").makeTwoWay())
),
// four small named ports, one on each side:
makePort("T", go.Spot.Top, false, true),
makePort("L", go.Spot.Left, true, true),
makePort("R", go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, true, false),
{ // handle mouse enter/leave events to show/hide the ports
mouseEnter: function(e, node) { showSmallPorts(node, true); },
mouseLeave: function(e, node) { showSmallPorts(node, false); }
}
);
function showSmallPorts(node, show) {
node.ports.each(function(port) {
if (port.portId !== "") { // don't change the default port, which is the big shape
port.fill = show ? "rgba(0,0,0,.3)" : null;
}
});
}
var linkSelectionAdornmentTemplate =
objGo(go.Adornment, "Link",
objGo(go.Shape,
// isPanelMain declares that this Shape shares the Link.geometry
{ isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }) // use selection object's strokeWidth
);
myDiagram.linkTemplate =
objGo(go.Link, // the whole link panel
{ selectable: true, selectionAdornmentTemplate: linkSelectionAdornmentTemplate },
{ relinkableFrom: true, relinkableTo: true, reshapable: true },
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4
},
new go.Binding("points").makeTwoWay(),
objGo(go.Shape, // the link path shape
{ isPanelMain: true, strokeWidth: 2 }),
objGo(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null }),
objGo(go.Panel, "Auto",
new go.Binding("visible", "isSelected").ofObject(),
objGo(go.Shape, "RoundedRectangle", // the link shape
{ fill: "#F8F8F8", stroke: null }),
objGo(go.TextBlock,
{
textAlign: "center",
font: "10pt helvetica, arial, sans-serif",
stroke: "#919191",
margin: 2,
minSize: new go.Size(10, NaN),
editable: true
},
new go.Binding("text").makeTwoWay())
)
);
load(); // load an initial diagram from some JSON text
// initialize the Palette that is on the left side of the page
myPalette =
objGo(go.Palette, "myPaletteDiv", // must name or refer to the DIV HTML element
{
maxSelectionCount: 1,
nodeTemplateMap: myDiagram.nodeTemplateMap, // share the templates used by myDiagram
linkTemplate: // simplify the link template, just in this Palette
objGo(go.Link,
{ // because the GridLayout.alignment is Location and the nodes have locationSpot == Spot.Center,
// to line up the Link in the same manner we have to pretend the Link has the same location spot
locationSpot: go.Spot.Center,
selectionAdornmentTemplate:
objGo(go.Adornment, "Link",
{ locationSpot: go.Spot.Center },
objGo(go.Shape,
{ isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }),
objGo(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null })
)
},
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4
},
new go.Binding("points"),
objGo(go.Shape, // the link path shape
{ isPanelMain: true, strokeWidth: 2 }),
objGo(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null })
),
model: new go.GraphLinksModel([ // specify the contents of the Palette
{ text: "Start", figure: "Circle", fill: "#00AD5F" },
{ text: "Step" },
{ text: "DB", figure: "Database", fill: "lightgray" },
{ text: "???", figure: "Diamond", fill: "lightskyblue" },
{ text: "End", figure: "Circle", fill: "#CE0620" },
{ text: "Comment", figure: "RoundedRectangle", fill: "lightyellow" }
], [
// the Palette also has a disconnected Link, which the user can drag-and-drop
{ points: new go.List(go.Point).addAll([new go.Point(0, 0), new go.Point(30, 0), new go.Point(30, 40), new go.Point(60, 40)]) }
])
});
}
function TopRotatingTool() {
go.RotatingTool.call(this);
}
go.Diagram.inherit(TopRotatingTool, go.RotatingTool);
/** @override */
TopRotatingTool.prototype.updateAdornments = function(part) {
go.RotatingTool.prototype.updateAdornments.call(this, part);
var adornment = part.findAdornment("Rotating");
if (adornment !== null) {
adornment.location = part.rotateObject.getDocumentPoint(new go.Spot(0.5, 0, 0, -30)); // above middle top
}
};
/** @override */
TopRotatingTool.prototype.rotate = function(newangle) {
go.RotatingTool.prototype.rotate.call(this, newangle + 90);
};
// end of TopRotatingTool class
// Show the diagram's model in JSON format that the user may edit
function save() {
saveDiagramProperties(); // do this first, before writing to JSON
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
loadDiagramProperties(); // do this after the Model.modelData has been brought into memory
}
function saveDiagramProperties() {
myDiagram.model.modelData.position = go.Point.stringify(myDiagram.position);
}
function loadDiagramProperties(e) {
// set Diagram.initialPosition, not Diagram.position, to handle initialization side-effects
var pos = myDiagram.model.modelData.position;
if (pos) myDiagram.initialPosition = go.Point.parse(pos);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width:100%; white-space:nowrap;">
<span style="display: inline-block; vertical-align: top; width:105px">
<div id="myPaletteDiv" style="border: solid 1px black; height: 620px"></div>
</span>
<span style="display: inline-block; vertical-align: top; width:80%">
<div id="myDiagramDiv" style="border: solid 1px black; height: 620px"></div>
</span>
</div>
<p>
This sample demonstrates the ability for the user to drag around a Link as if it were a Node.
When either end of the link passes over a valid port, the port is highlighted.
</p>
<p>
The link-dragging functionality is enabled by setting some or all of the following properties:
<a>DraggingTool.dragsLink</a>, <a>LinkingTool.isUnconnectedLinkValid</a>, and
<a>RelinkingTool.isUnconnectedLinkValid</a>.
</p>
<p>
Note that a Link is present in the <a>Palette</a> so that it too can be dragged out and onto
the main Diagram. Because links are not automatically routed when either end is not connected
with a Node, the route is provided explicitly when that Palette item is defined.
</p>
<p>
This also demonstrates several custom Adornments:
<a>Part.selectionAdornmentTemplate</a>, <a>Part.resizeAdornmentTemplate</a>, and
<a>Part.rotateAdornmentTemplate</a>.
</p>
<p>
Finally this sample demonstrates saving and restoring the <a>Diagram.position</a> as a property
on the <a>Model.modelData</a> object that is automatically saved and restored when calling <a>Model.toJson</a>
and <a>Model.fromJson</a>.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
],
"linkDataArray": [
]}
</textarea>
</div>
</div>
</body>
</html>
效果如下:

建議各位copy代碼,在本地看到效果,然后再根據(jù)實(shí)際需求去研究它的api,這樣才不會(huì)太盲目而花費(fèi)太多時(shí)間。
2、工業(yè)流程圖
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Process Flow</title>
<meta name="description" content="A simple process flow or SCADA diagram editor, simulating equipment monitoring and control." />
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="../../gojs/go-debug.js"></script>
<script id="code">
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make; // for more concise visual tree definitions
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"grid.visible": true,
"grid.gridCellSize": new go.Size(30, 20),
"draggingTool.isGridSnapEnabled": true,
"resizingTool.isGridSnapEnabled": true,
"rotatingTool.snapAngleMultiple": 90,
"rotatingTool.snapAngleEpsilon": 45,
"undoManager.isEnabled": true
});
// when the document is modified, add a "*" to the title and enable the "Save" button
myDiagram.addDiagramListener("Modified", function(e) {
var button = document.getElementById("SaveButton");
if (button) button.disabled = !myDiagram.isModified;
var idx = document.title.indexOf("*");
if (myDiagram.isModified) {
if (idx < 0) document.title += "*";
} else {
if (idx >= 0) document.title = document.title.substr(0, idx);
}
});
myDiagram.nodeTemplateMap.add("Process",
$(go.Node, "Auto",
{ locationSpot: new go.Spot(0.5, 0.5), locationObjectName: "SHAPE",
resizable: true, resizeObjectName: "SHAPE" },
new go.Binding("location", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Cylinder1",
{ name: "SHAPE",
strokeWidth: 2,
fill: $(go.Brush, "Linear",
{ start: go.Spot.Left, end: go.Spot.Right,
0: "gray", 0.5: "white", 1: "gray" }),
minSize: new go.Size(50, 50),
portId: "", fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides
},
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
$(go.TextBlock,
{ alignment: go.Spot.Center, textAlign: "center", margin: 5,
editable: true },
new go.Binding("text").makeTwoWay())
));
myDiagram.nodeTemplateMap.add("Valve",
$(go.Node, "Vertical",
{ locationSpot: new go.Spot(0.5, 1, 0, -21), locationObjectName: "SHAPE",
selectionObjectName: "SHAPE", rotatable: true },
new go.Binding("angle").makeTwoWay(),
new go.Binding("location", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.TextBlock,
{ alignment: go.Spot.Center, textAlign: "center", margin: 5, editable: true },
new go.Binding("text").makeTwoWay(),
// keep the text upright, even when the whole node has been rotated upside down
new go.Binding("angle", "angle", function(a) { return a === 180 ? 180 : 0; }).ofObject()),
$(go.Shape,
{ name: "SHAPE",
geometryString: "F1 M0 0 L40 20 40 0 0 20z M20 10 L20 30 M12 30 L28 30",
strokeWidth: 2,
fill: $(go.Brush, "Linear", { 0: "gray", 0.35: "white", 0.7: "gray" }),
portId: "", fromSpot: new go.Spot(1, 0.35), toSpot: new go.Spot(0, 0.35) })
));
myDiagram.linkTemplate =
$(go.Link,
{ routing: go.Link.AvoidsNodes, curve: go.Link.JumpGap, corner: 10, reshapable: true, toShortLength: 7 },
new go.Binding("points").makeTwoWay(),
// mark each Shape to get the link geometry with isPanelMain: true
$(go.Shape, { isPanelMain: true, stroke: "black", strokeWidth: 5 }),
$(go.Shape, { isPanelMain: true, stroke: "gray", strokeWidth: 3 }),
$(go.Shape, { isPanelMain: true, stroke: "white", strokeWidth: 1, name: "PIPE", strokeDashArray: [10, 10] }),
$(go.Shape, { toArrow: "Triangle", fill: "black", stroke: null })
);
load();
loop(); // animate some flow through the pipes
}
function loop() {
var diagram = myDiagram;
setTimeout(function() {
var oldskips = diagram.skipsUndoManager;
diagram.skipsUndoManager = true;
diagram.links.each(function(link) {
var shape = link.findObject("PIPE");
var off = shape.strokeDashOffset - 2;
shape.strokeDashOffset = (off <= 0) ? 20 : off;
});
diagram.skipsUndoManager = oldskips;
loop();
}, 100);
}
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px"></div>
<p>
A <em>process flow diagram</em> is commonly used in chemical and process engineering to indicate the general flow of plant processes and equipment.
A simple SCADA diagram, with animation of the flow along the pipes, is implemented here.
</p>
<p>
The diagram displays the background grid layer by setting <b>grid.visible</b> to true,
and also allows snapping to the grid using <a>DraggingTool.isGridSnapEnabled</a>,
<a>ResizingTool.isGridSnapEnabled</a>, and <a>RotatingTool.snapAngleMultiple</a> alongside <a>RotatingTool.snapAngleEpsilon</a>.
</p>
<p>
The diagram also uses the <b>loop</b> function to animate the links by adjusting the <a>Shape.strokeDashOffset</a> every 100 ms.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{"key":"P1", "category":"Process", "pos":"150 120", "text":"Process"},
{"key":"P2", "category":"Process", "pos":"330 320", "text":"Tank"},
{"key":"V1", "category":"Valve", "pos":"270 120", "text":"V1"},
{"key":"P3", "category":"Process", "pos":"150 420", "text":"Pump"},
{"key":"V2", "category":"Valve", "pos":"150 280", "text":"VM", "angle":270},
{"key":"V3", "category":"Valve", "pos":"270 420", "text":"V2", "angle":180},
{"key":"P4", "category":"Process", "pos":"450 140", "text":"Reserve Tank"},
{"key":"V4", "category":"Valve", "pos":"390 60", "text":"VA"},
{"key":"V5", "category":"Valve", "pos":"450 260", "text":"VB", "angle":90}
],
"linkDataArray": [
{"from":"P1", "to":"V1"},
{"from":"P3", "to":"V2"},
{"from":"V2", "to":"P1"},
{"from":"P2", "to":"V3"},
{"from":"V3", "to":"P3"},
{"from":"V1", "to":"V4"},
{"from":"V4", "to":"P4"},
{"from":"V1", "to":"P2"},
{"from":"P4", "to":"V5"},
{"from":"V5", "to":"P2"}
]}
</textarea>
</div>
</div>
</body>
</html>
工業(yè)流程圖

四、總結(jié)
本文根據(jù)js的一些基礎(chǔ)用法做了簡(jiǎn)單介紹,今天就先到這里,以后有問(wèn)題了再來(lái)跟大家分享。如果你的項(xiàng)目里面也有這種業(yè)務(wù)需求,可以用起來(lái)試試!需要說(shuō)明一點(diǎn),如果您的公司不缺錢,建議使用正版授權(quán)的組件,畢竟尊重作者的勞動(dòng)成果很重要!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用OPENLAYERS3實(shí)現(xiàn)點(diǎn)選的方法
這篇文章主要為大家詳細(xì)介紹了使用OPENLAYERS3實(shí)現(xiàn)點(diǎn)選的幾種方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
微信小程序movable-view的可移動(dòng)范圍示例詳解
這篇文章主要介紹了微信小程序movable-view的可移動(dòng)范圍?,movable-view不管怎么移動(dòng)都要完全包含住movable-area,也就是說(shuō)movable-area不能超出movable-view的區(qū)域范圍,反之亦然,本文通過(guò)示例介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
JS.GetAllChild(element,deep,condition)使用介紹
JS.GetAllChild()獲取所有子節(jié)點(diǎn),想必大家都知道吧,具體的使用方法如下,感興趣的朋友可以參考下2013-09-09
layui實(shí)現(xiàn)點(diǎn)擊按鈕給table添加一行
想實(shí)現(xiàn)點(diǎn)擊按鈕在表格添加一行的功能,但發(fā)現(xiàn)layui并未集成該工具欄,因此,需要自己手動(dòng)添加這個(gè)功能;這篇文章主要介紹了layui點(diǎn)擊按鈕給table添加一行,需要的朋友可以參考下2018-08-08
JS實(shí)現(xiàn)數(shù)組扁平化的方法分享
數(shù)組扁平化指的是:將一個(gè)多層嵌套的數(shù)組,處理成只有一層的數(shù)組。本文主要和大家介紹了幾個(gè)常用的JS數(shù)組扁平化方法,希望對(duì)大家有所幫助2023-04-04

