流程圖控件GoJS教程:擴展GoJS
GoJS是一款功能強大,快速且輕量級的流程圖控件,可幫助你在JavaScript 和HTML5 Canvas程序中創(chuàng)建流程圖,且極大地簡化您的JavaScript / Canvas 程序。
GoJS可以通過多種方式進行擴展。更改標(biāo)準(zhǔn)行為的最常見方法是在GraphObject,Diagram,CommandHandler,Tool或Layout上設(shè)置屬性。但是,當(dāng)不存在此類屬性時,您可能需要覆蓋CommandHandler,Tool,Layout,Link或Node的方法。API參考中記錄了可以覆蓋的方法。
- 不要修改GoJS類的原型。
- 僅使用API中記錄的屬性和方法。
請注意,擴展類的API可能會隨任何版本(甚至是點發(fā)行)而更改。如果打算在生產(chǎn)環(huán)境中使用擴展名,則應(yīng)將代碼復(fù)制到自己的源目錄中。
除了我們的示例外,GoJS還提供了一個擴展庫,展示了自定義工具和布局的創(chuàng)建。這些類和示例已被翻譯成TypeScript,可從此處獲得../extensionsTS/。這些擴展類是UMD模塊。他們使用../release/go.js圖書館。擴展類還可以作為ES6模塊在以下位置獲得../extensionsJSM/:這些使用../release/go-module.js庫。我們建議您將所需的文件復(fù)制到項目中,以便可以調(diào)整它們對“ go.js”庫的引用方式,并可以將它們包含在自己的構(gòu)建和打包過程中。
命令處理程序
通過覆蓋CommandHandler,您可以更改默認(rèn)功能并創(chuàng)建自己的鍵綁定。有關(guān)更多信息,請參見“ 命令 ” 簡介頁。但是,下面顯示的用于覆蓋“工具和布局”上的方法的技術(shù)也適用于CommandHandler。
工具和布局
GoJS使用許多工具和布局在節(jié)點和鏈接上進行操作,所有這些工具和布局都是Tool和Layout類的子類。有關(guān)工具的更多信息,請參見工具的簡介頁面;有關(guān)布局的更多信息,請參見布局的簡介頁面。
可以修改工具,也可以將其替換或添加到Diagram.toolManager中。所有工具必須繼承自Tool類或繼承自Tool的類。
我們的一些示例(例如Pipes示例)包含自定義工具的示例。擴展庫中提供了更多自定義工具示例。這些類和示例的TypeScript版本可以在../extensionsTS/中找到。這些自定義工具類也可以作為ES6模塊在../extensionsJSM/中使用。布局可以被修改,或者它們可以通過設(shè)置使用Diagram.layout或Group.layout。所有布局都必須從Layout類或從Layout繼承的類繼承。
我們的一些示例(例如“ 解析樹”示例)包含自定義布局的示例。擴展庫中提供了更多自定義布局示例。這些類的TypeScript版本可以在../extensionsTS/中找到。這些自定義布局類也可以在../extensionsJSM/中的ES6模塊中使用。
在不定義子類的情況下覆蓋方法
通常,我們可以避免完整地繼承Tool的子類,而僅重寫單個方法。當(dāng)我們想對方法的行為做些小改動時,這是很常見的。這里我們展示了如何覆蓋ClickSelectingTool ,standardMouseSelect方法通過修改特定圖表的工具實例。
也可以用這種方式覆蓋Layout方法,但很少這樣做——布局幾乎總是被子類化。對于Group.layout值的布局,無法完成此操作,因為這些布局可能會被復(fù)制且無法共享。因為我們不是在創(chuàng)造一個新的(子)類,所以我們直接在圖的ClickSelectingTool上設(shè)置這個方法,這個工具是通過它的工具管理器引用的。以這種方式覆蓋方法的典型腳手架如下:
var tool = diagram.toolManager.clickSelectingTool; tool.standardMouseSelect = function() { //可能在 // ... 之前做其他事情 //注意在此類函數(shù)中使用“ this”! //如果您想要正常的行為,請調(diào)用基本功能。 //注意對原型的引用, //以及對“ call”的調(diào)用,將其傳遞為“ this”。 go.ClickSelectingTool.prototype.standardMouseSelect.call(tool); //也許在 // 之后做其他事情... }作為一個具體的例子,我們將覆蓋工具standardMouseSelect只選擇寬度和高度小于50圖單元的節(jié)點和鏈接。這意味著我們必須使用圖表找到要選擇的對象。findPartAt,檢查它的邊界,如果邊界太大就退出。否則,我們調(diào)用基本功能來進行選擇。
diagram.nodeTemplate = $(go.Node,“ Auto”, $(go.Shape,“ Rectangle”, { fill:“ white” }, 新建 go.Binding(“ fill”,“ color”)), $(go.TextBlock, { margin:5 }, 新的 go.Binding(“ text”,“ key”)) ); var tool = diagram.toolManager.clickSelectingTool; tool.standardMouseSelect = function() { var diagram = tool.diagram; var e = diagram.lastInput; //以選擇包含組如果Part.canSelect()為假 VAR curobj = diagram.findPartAt(e.documentPoint,false); 如果(curobj!== null){ var bounds = curobj.actualBounds; //如果邊界大于50寬度或高度, 則結(jié)束選擇過程,如果(bounds.width> 50 || bounds.height> 50){ //如果這是沒有修飾符的左鍵單擊,則我們希望執(zhí)行相同的操作仿佛 // //點擊“圖表”背景,因此 如果((e.left &&!e.control &&!e.shift){ diagram.clearSelection(); } //然后返回,這樣就不會調(diào)用基本功能 return; } } //否則,調(diào)用基本功能 go.ClickSelectingTool.prototype.standardMouseSelect.call(tool); } diagram.model = new go.Model([ { 鍵:“ Alpha”,顏色:“淺藍色” }, { 鍵:“ Epsilon”,顏色:“薊” }, { 鍵:“ Psi”,顏色:“ lightcoral” }, { 鍵:“伽瑪”,顏色:“ 淺綠色” } ]);
運行此代碼,我們看到“ Epsilon”和“ Gamma”節(jié)點都不可選,因為它們的寬度都大于50。請注意,此自定義工具不會更改其他可能選擇的工具的行為,例如DraggingTool或該DragSelectingTool。
通過子類化布局覆蓋方法
可以將布局子類化以創(chuàng)建自定義布局,這些自定義布局繼承現(xiàn)有布局的屬性和方法。GoJS的傳統(tǒng)JavaScript中的子類化需要幾個關(guān)鍵步驟:
- 創(chuàng)建一個新類(函數(shù)),然后調(diào)用基類構(gòu)造函數(shù)。
- 使用新類和基類調(diào)用Diagram.inherit。
- 修改派生類的原型以添加新功能。
function CascadeLayout() { //注意直接調(diào)用構(gòu)造函數(shù),不使用“原型” go.Layout.call(this); //在“ this”上添加新屬性 } go.Diagram.inherit(CascadeLayout,go.Layout); // //注意在原型 CascadeLayout.prototype.doLayout = function(coll)上設(shè)置方法 { //布局邏輯在這里。 //您可以可靠地使用“ this”來引用 //調(diào)用此方法的布局實例。 }請注意,如果您使用現(xiàn)代JavaScript(ECMAScript 6)或TypeScript進行編寫,則可以使用更新的語法來定義類:
導(dǎo)出 類 CascadeLayout 擴展 go。布局 { //在此聲明和初始化新的數(shù)據(jù)屬性(字段) constuctor(){ super(); //其他初始化可以在這里完成 } //(可選)在此處定義屬性獲取器和設(shè)置器 //覆蓋或定義方法 公共doLayout(coll){ //布局邏輯在這里。 } }布局通常需要用作布局選項的其他屬性。要向中添加“ offset”屬性CascadeLayout,我們將使用下劃線成員為私有的約定,并在構(gòu)造函數(shù)中設(shè)置默認(rèn)值:
function CascadeLayout() { go.Layout.call(this); 此 ._offset = 新 go.Size(12,12); }然后,我們使用Object.defineProperty“公開”獲取器和設(shè)置器。Getter和Setters使我們能夠進行類型檢查并具有副作用。此設(shè)置器確保偏移值是一個go.Size對象,并且僅在更改值后才使布局無效。
Object .defineProperty(CascadeLayout.prototype,“ offset”,{ get:function() { 返回 此 ._offset; }, get:function(val) { if(?。╲al instanceof go.Size)){ 拋出 新 錯誤(“ CascadeLayout.offset的新值必須是Size,而不是:” + val); } if(!this ._offset.equals(val)){ this ._offset = val; 這個 .invalidateLayout(); } } });如果您使用ECMAScript 6或TypeScript進行編寫,則可以定義屬性getter和setter。
get offset(){ 返回 此 ._offset; } set offset(val){ if(!(val instanceof go.Size)){ 拋出 新 錯誤(“ CascadeLayout.offset的新值必須為Size,而不是:” + val); } if(!this ._offset.equals(val)){ this ._offset = val; 這個 .invalidateLayout(); } }如果可以將布局用作Group.layout的值,則需要確??梢哉_復(fù)制在Group模板中設(shè)置的實例。
CascadeLayout.prototype.cloneProtected = function(復(fù)制) { go.Layout.prototype.cloneProtected.call(this,copy); copy._offset = 此 ._offset; }最后,我們將定義一個Layout.doLayout,確保查看文檔并適應(yīng)所有可能的輸入,因為doLayout具有一個參數(shù),可以是Diagram,Group或Iterable集合。
綜上,我們可以看到級聯(lián)布局的實際效果:
/ ** * @構(gòu)造函數(shù) * @擴展布局 * @類 *此布局按offset屬性指定的級聯(lián)排列節(jié)點 * / function CascadeLayout() { go.Layout.call(this); 此 ._offset = new go.Size(12,12); } go.Diagram.inherit(CascadeLayout,go.Layout); CascadeLayout.prototype.cloneProtected = function(復(fù)制) { go.Layout.prototype.cloneProtected.call(this,copy); copy._offset = 此 ._offset; } Object .defineProperty(CascadeLayout.prototype,“ offset”,{ get:function() { 返回 此 ._offset; }, get:function(val) { if(?。╲al instanceof go.Size)){ 拋出 新 錯誤(“ CascadeLayout.offset的新值必須是Size,而不是:” + val); } if(!this ._offset.equals(val)){ this ._offset = val; this .invalidateLayout(); } } }); / ** *此方法放置所有節(jié)點,并忽略所有鏈接。 * @this {CascadeLayout} * @param {Diagram | Group | Iterable}將零件集合收集到布局中。 * / CascadeLayout.prototype.doLayout = function(coll) { //獲取要布置的節(jié)點和鏈接 var parts = this .collectParts(coll); //從布局的起點開始布局,這是從Layout繼承的屬性 var x = this .arrangementOrigin.x; var y = 此 .arrangementOrigin.y; var offset = this .offset; var it = parts.iterator; while(it.next()){ var node = it.value; if((node instanceof go.Node))continue ; //忽略鏈接 node.move(new go.Point(x,y)); x + = offset.width; y + = offset.height; } } // CascadeLayout的結(jié)尾 //常規(guī)圖設(shè)置: diagram.layout = new CascadeLayout(); diagram.nodeTemplate = $(go.Node,“ Auto”, $(go.Shape,“ Rectangle”, { fill:“ white” }, new go.Binding(“ fill”,“ color”)), $(go.TextBlock, { margin:5 }, new go.Binding(“ text”,“ key”)) ); diagram.model = new go.Model([ { 鍵:“ Alpha”,顏色:“淺藍色” }, { 鍵:“貝塔”,顏色:“薊” }, { 鍵:“ Delta”,顏色:“ lightcoral” }, { 鍵:“伽瑪”,顏色:“ 淺綠色” } ]);