目錄
- 基礎概念
- 開始繪制圖形
- [1. 通過代碼構建圖形](#1. 通過代碼構建圖形)
- [2. 通過 GraphObject.make 構建圖形](#2. 通過 GraphObject.make 構建圖形)
- [3. 使用 Model 和 Templates 創建圖形](#3. 使用 Model 和 Templates 創建圖形)
- 獲取圖形數據
- [獲取所有 Node、Link](#獲取所有 Node、Link)
- diagram.model.toJSON()
- [獲取指定的 Node,Link](#獲取指定的 Node,Link)
- 獲取選擇元素信息
- 修改圖形
- 事件
- Transactions
- [Template Maps](#Template Maps)
- [Palette Diagrams](#Palette Diagrams)
- 在Vue中使用GOJS
GoJS 和 GO 語言沒有關系,它是一個用來創建交互式圖表的 JavaScript 庫。
基礎概念
GraphObject 是所有圖形是抽象基類,基本上 GoJS 中,萬物皆 GraphObject。
Panel 有不同的類型,每個類型表示一種布局,通過不同的坐標系統排列。
注意,GoJS 中的 x 軸水平向右,y 軸垂直向下。
包括:
- Panel.Position 建立坐標系,通過指定坐標對絕對位置排序。
- Panel.Vertical & Panel.Horizontal 顧名思義,分別在垂直和水平的線性排列
- Panel.Auto 調整主元素的大小以適應 Panel 中的其他元素。
- Panel.Spot 通過 GraphObject.alignment 屬性指定其他元素相對於主元素的位置。
- Panel.Table 以表格的方式排列元素,通過指定 row 和 col 以及相關信息指定元素位置。
- Panel.TableRow & Panel.TableColumn 只能在 Panel.Table 中使用,以將元素集合組織為表格中的行或列。
- Panel.Viewbox 用於自動調整 ** 單個元素 ** 的大小以適合面板的可用區域。
- Panel.Grid 僅用於繪制常規的線條圖案。元素必須是用於描述重復行的形狀。
- Panel.Link 僅供 Link 部件和 Link Adornments 使用。
- Panel.Graduated 用於沿主 Shape 元素繪制常規刻度線和文本。
Part 以及他的繼承元素 Node 和 Link 等可以直接被加到 diagram 中(通過 Diagram.add
)。其他的元素要作為 Part 的子元素。
Node 可以被 Link 連接起來。每一個 Node 都有一個 key,用來唯一標識該 Node。link 有 from 和 to 屬性,用來表示該 Link 連接了哪兩個邊。
Group 是一個 Node,可以包含一組 Nodes 和 Links。
Shape 表示一個幾何圖形。可以使用 GoJS 中定義好的一些圖形,如 “Rectangle” 也可以自定義圖形的形狀。通過 fill 和 stroke 等屬性決定圖形的顯示。
TextBlock 用來顯示文本。字體屬性和 CSS 相同 font-style font-variant font-weight font-size font-family
。也可以定義 stroke 等屬性。
開始繪制圖形
1. 通過代碼構建圖形
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://unpkg.com/gojs/release/go.js"></script>
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px blue; width:400px; height:150px"></div>
<script>
// 創建圖
var diagram = new go.Diagram("myDiagramDiv");
// 創建節點 節點中元素的排列方式是 Panel.Auto
var node = new go.Node(go.Panel.Auto);
// 創建圖形
var shape = new go.Shape();
// 定義圖形屬性
shape.figure = "RoundedRectangle";
shape.fill = "lightblue";
// 將圖形加到節點
node.add(shape);
// 創建一個文本
var textblock = new go.TextBlock();
// 定義文本屬性
textblock.text = "Hello!";
textblock.margin = 5;
// 文本加到節點
node.add(textblock);
// 將節點加到圖
diagram.add(node);
</script>
</body>
</html>
2. 通過 GraphObject.make 構建圖形
GraphObject.make
是一個神奇的函數,詳見 GraphObject.make,可以用來創建各種 GraphObject
元素,同時定義元素的屬性。可以嵌套定義多個元素。
上面代碼也可以寫為下面的方式(因為 go.GraphObject.make
使用較多,所以官網建議命名為 $
方便使用,你當然也可以起其他的名字)
var diagram = new go.Diagram("myDiagramDiv");
var $ = go.GraphObject.make;
diagram.add(
$(go.Node, go.Panel.Auto,
$(go.Shape,
{ figure: "RoundedRectangle", fill: "lightblue" }),
$(go.TextBlock,
{ text: "Hello!", margin: 5 })
));
好處一方面是代碼量減少了,另一方面如果給元素賦值了錯誤的屬性,make
函數會報出錯誤。
例如,如果 text 寫錯為 test,會在控制台看到報錯:
Uncaught Error: Trying to set undefined property "test" on object: TextBlock("")
3. 使用 Model 和 Templates 創建圖形
雖然第二種方式很簡單,但是如果要加很多的 Node,而不同的 Node 之間結構相同,通過上面的方式,就需要 add 好多次。如果能定義一個 Node 的模板,然后通過指定參數來定義節點,就會方便很多。
這就類似定義一個類,以后只需要通過 new Something(p1, p2...) 來將創建一類對象。
舉例,
var diagram = new go.Diagram("myDiagramDiv");
var $ = go.GraphObject.make;
diagram.nodeTemplate = // 定義模板
$(go.Node, "Auto",
$(go.Shape,
{ figure: "RoundedRectangle", fill: "white" }),
$(go.TextBlock,
{ text: "hello!", margin: 5 })
);
var nodeDataArray = [
{ key: "Alpha" },
{ key: "Beta" }
];
var linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
通過 go.GraphLinksModel
來創建一個圖需要分別指定 Node 和 Link 的集合。定義好模板(diagram.nodeTemplate
)后,只需要為 diagram.model
傳入 nodeDataArray,數組中每一個元素都在圖中對應一個節點和 linkDataArray,對應每一條邊。
如果 Node 的結構相同,只是某幾個屬性不同,也可以在模板中定義變量。變量通過 go.Binding 來定義。
var diagram = new go.Diagram("myDiagramDiv");
var $ = go.GraphObject.make;
diagram.nodeTemplate = // 定義模板
$(go.Node, "Auto",
$(go.Shape,
{ figure: "RoundedRectangle", fill: "white" }, // white 作為 fill 的默認屬性
new go.Binding('fill', 'myFill') // 在 model 的 nodes 中通過 myFill 屬性來指定 Node 的 fill 屬性
),
$(go.TextBlock,
{ text: "hello!", margin: 5 })
);
var nodeDataArray = [
{ key: "Alpha" }, // 默認白色節點
{ key: "Beta", myFill: 'green' } // 綠色節點
];
var linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
獲取圖形數據
獲取所有 Node、Link
通過 diagram.model.nodeDataArray
會獲得所有節點的集合。
[{"key":"Alpha","__gohashid":408},{"key":"Beta","myFill":"green","__gohashid":409}]
會發現節點的屬性除了用戶賦值的以外,還有一個 __gohashid
,這個是 GoJS 維護的一個屬性,不需要去管。
通過 diagram.model.linkDataArray
會獲得所有邊的集合。
通過 diagram.nodes
獲得的是所有 Nodes 和 Groups 的迭代器 Iterator<Node>
。遍歷迭代器:
for (var it = diagram.nodes; it.next(); ) {
var node = it.value;
console.log(node);
}
diagram.links
同理。
注意,通過 diagram 獲得的節點和 model 中的不同,這樣獲得的節點包含很多圖中的其他信息,比如對於邊,包含相連的節點信息等。
diagram.model.toJSON()
可以通過 diagram.model.toJSON() 獲取圖形相關數據,其中下划線開頭的屬性會被忽略,自然也會忽略 __gohashid
,所以起自己的屬性名時,不要以下划線開頭。
> diagram.model.toJSON()
< "{ "class": "GraphLinksModel",
"nodeDataArray": [
{"key":"Alpha"},
{"key":"Beta", "myFill":"green"}
],
"linkDataArray": [ {"from":"Alpha", "to":"Beta"} ]
}"
使用 diagram.model = go.Model.fromJson(model);
可以使用 model 字符串加載圖。
獲取指定的 Node,Link
獲取指定 key 的 Node: findNodeDataForKey
> diagram.model.findNodeDataForKey('Alpha')
< {key: "Alpha", __gohashid: 408}
獲取指定 key 的 Link findLinkDataForKey
,默認 GoJS 中的 Link 是不會被賦值一個唯一的 key 的,除非設置 linkKeyProperty
。
也可以通過 diagram 查找節點。
> diagram.findNodeForKey('Alpha')
< V {__gohashid: 457, G: 4194307, lb: 1, bg: null, Ua: "", …}
> diagram.findNodeForKey('Alpha').data === diagram.model.findNodeDataForKey('Alpha')
< true
在 diagram 中查找邊 diagram.findLinkForData
,不過查找的 data 要指定所有的屬性,包括 __gohashid
。
> diagram.findLinkForData({from: "Alpha", to: "Beta",__gohashid: 411})
< S {__gohashid: 469, G: 4194307, lb: 1, bg: null, Ua: "", …}
> link = diagram.findLinkForData(diagram.model.linkDataArray[0])
< S {__gohashid: 469, G: 4194307, lb: 1, bg: null, Ua: "", …}
> link.data
< {from: "Alpha", to: "Beta", __gohashid: 411}
> link.fromNode
< V {__gohashid: 457, G: 4194307, lb: 1, bg: null, Ua: "", …}
獲取選擇元素信息
diagram.selection
是一個只讀屬性,返回被選中元素(包括節點和邊)集合。
獲得選中個數 diagram.selection.size
獲取第一個選中的元素,diagram.selection.first()
修改圖形
修改節點屬性
直接修改 model
中的屬性值不會使圖形發生改變的,要通過 setDataProperty
進行修改。
var data = myDiagram.model.findNodeDataForKey("Alpha");
// 直接修改不生效
if (data !== null) data.color = "red";
// 通過 diagram.model.setDataProperty 修改才可以
diagram.model.setDataProperty(data, 'myFill', 'purple');
選中元素
通過代碼指定選中元素。
var node = diagram.findNodeForKey('Alpha');
// 會清除當前選中並選中 node
diagram.select(node);
刪除、添加節點和邊
var node = { key: 'addNode', myFill: 'tomato' };
// 添加節點
diagram.model.addNodeData(node);
// 添加邊 Alpha -> addNode
diagram.model.addLinkData({from:'Alpha', to: 'addNode'});
// 刪除節點
diagram.model.removeNodeData(node);
事件
Model 和 Diagram 都會產生事件。所以可以通過 Model.addChangedListener
和 Diagram.addChangedListener
添加事件監聽器。通過 removeChangedListener
刪除事件監聽。
let listener = function(changedEvent) {
// do something...
}
// 添加監聽器
diagram.addModelChangedListener(listener);
// 移除監聽器
diagram.removeModelChangedListener(listener);
通過 Model.setDataProperty
修改元素的屬性,圖形的結構不會發生變化 changedEvent.modelChange
會是一個空字符串。
diagram.addModelChangedListener(function(changedEvent) {
// 修改的元素對象(修改后) 修改屬性 修改之前的值 修改之后的值
if (changedEvent.modelChange === '') {
console.log(changedEvent.object, changedEvent.propertyName, changedEvent.oldValue, changedEvent.newValue);
}
});
其他情況下 changedEvent.modelChange
會是下面的字符串之一:
- "nodeDataArray", 修改
Model.nodeDataArray
包括diagram.model.nodeDataArray = [...]
,Model.addNodeData
或Model.removeNodeData
- "nodeCategory", 調用
Model.setCategoryForNodeData
- "nodeGroupKey",
GraphLinksModel.setGroupKeyForNodeData
- "linkDataArray", 修改
GraphLinksModel.linkDataArray
,包括diagram.model.nodeDataArray = [...]
,GraphLinksModel.addLinkData
或GraphLinksModel.removeLinkData
- "linkFromKey", 調用
GraphLinksModel.setFromKeyForLinkData
- "linkToKey", 調用
GraphLinksModel.setToKeyForLinkData
- "linkFromPortId", 調用
GraphLinksModel.setFromPortIdForLinkData
- "linkToPortId", 調用
GraphLinksModel.setToPortIdForLinkData
- "linkLabelKeys", 調用
GraphLinksModel.setLabelKeysForLinkData
,GraphLinksModel.addLabelKeyForLinkData
或GraphLinksModel.removeLabelKeyForLinkData
- "linkCategory", 調用
GraphLinksModel.setCategoryForLinkData
- "nodeParentKey", 調用
TreeModel.setParentKeyForNodeData
- "parentLinkCategory", 調用
TreeModel.setParentLinkCategoryForNodeData
事務相關的事務, ChangedEvent.propertyName
會是以下的值。它們的 changedEvent.modelChange
也是一個空字符串。
- "StartingFirstTransaction"
- "StartedTransaction"
- "CommittingTransaction"
- "CommittedTransaction"
- "RolledBackTransaction"
- "StartingUndo"
- "FinishedUndo"
- "StartingRedo"
- "FinishedRedo"
可以通過 isTransactionFinished
判斷事件是否是結束。
diagram.addModelChangedListener(function(evt) {
if (evt.isTransactionFinished) saveModel(evt.model);
});
Transactions
因為 GoJS 有 undo 和 redo 的功能,所以在撤銷和恢復時,要保證單步操作一系列修改的完整性。
但是原則上,每次使用代碼來對圖形進行修改,都應該用事務包裹起來,即使不開啟 undo/redo 功能。
開啟 undo & redo 功能 Ctrl-Z(撤銷)/ Ctrl-Y(恢復)
diagram.model.undoManager.isEnabled = true;
// 或
diagram =
$(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true // undo & redo
});
使用事物:
diagram.commit(function(d) {
// ....
}, "do a transaction");
Template Maps
上面的節點模板都只有一種,通過 diagram.nodeTemplate
來定義。當一個圖形中希望有多種表現形式的節點時變不夠用了。這時可以使用 myDiagram.nodeTemplateMap
來定義多種模板,並在使用時通過 category
指定。
var template1 =
$(go.Node, "Auto",
$(go.Shape,
{ figure: "RoundedRectangle", fill: "white" }),
$(go.TextBlock,
{ text: "hello!", margin: 5 },
new go.Binding('text', 'text')
));
var template2 =
$(go.Node, "Auto",
$(go.Shape, "RoundedRectangle",
new go.Binding("fill", "color")),
$(go.Panel, "Table",
{ defaultAlignment: go.Spot.Left },
$(go.TextBlock, { row: 0, column: 0, columnSpan: 2, font: "bold 12pt sans-serif" },
new go.Binding("text", "key")),
$(go.TextBlock, { row: 1, column: 0 }, "Description:"),
$(go.TextBlock, { row: 1, column: 1 }, new go.Binding("text", "desc")),
$(go.TextBlock, { row: 2, column: 0 }, "Color:"),
$(go.TextBlock, { row: 2, column: 1 }, new go.Binding("text", "color"))
)
);
// 設置 category 為空字符串 則為不指定 category 時的默認圖形
diagram.nodeTemplateMap.add('', template1);
// category: detailtemplate
diagram.nodeTemplateMap.add('detailtemplate', template2);
/* 或者定義一個 go.Map 並賦值給 diagram.nodeTemplateMap
var templmap = new go.Map();
templmap.add("", template1);
templmap.add("detailtemplate", template2);
diagram.nodeTemplateMap = templmap;
*/
var nodeDataArray = [
{ key: "Alpha", text: 'Alpha' },
{ key: "Beta", text: 'Beta', color: 'pink', desc: 'desc', category: 'detailtemplate' }
];
var linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
Palette Diagrams
創建 go.Palette 來創建圖形模板,可以通過拖拽的方式快速生成一系列相同的圖案。
在Vue中使用GOJS
官網例子很清晰 不需要贅述