成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

mxGraph 入門實(shí)例教程

coordinate35 / 2748人閱讀

摘要:本教程會(huì)使用到語法,而第二部分的項(xiàng)目是用寫的。閱讀本教程需要你掌握這兩項(xiàng)預(yù)備知識(shí)。在中可以代表組節(jié)點(diǎn)邊,這個(gè)類封裝了的操作,本教程不涉及到組的內(nèi)容。表示在邊的正交線上移到的距離。

在上一篇文章 《記一次繪圖框架技術(shù)選型: jsPlumb VS mxGraph》 中,提到了我為什么要去學(xué)習(xí) mxGraph。在入門時(shí)我遇到了以下幾個(gè)問題

官方文檔偏向理論,沒能較好地結(jié)合代碼進(jìn)行講解

雖然官方給出的例子很多,但沒有說明閱讀順序,對(duì)剛?cè)腴T的我不知道應(yīng)該從哪開始閱讀

通過搜索引擎搜索 “mxGraph教程” 沒能得到太大幫助

通過自己對(duì)著官方文檔死磕了一段時(shí)間并在公司項(xiàng)目中進(jìn)行實(shí)踐后,慢慢開始掌握這個(gè)框架的使用。下面我就根據(jù)我的學(xué)習(xí)經(jīng)驗(yàn)寫一篇比較適合入門的文章。

官方列了比較多文檔,其中下面這幾份是比較有用的。

mxGraph Tutorial,這份文檔主要講述整個(gè)框架的組成

mxGraph User Manual – JavaScript Client,這份文檔對(duì)一些重要的概念進(jìn)行講解,以及介紹一些重要的 API

在線實(shí)例,這些實(shí)例的源碼都在這里有

API 文檔,這是最重要的一份文檔,在接下來的教程我不會(huì)對(duì)接口作詳細(xì)講述,你可以在這里對(duì)相關(guān)接口作深入了解

在看完我的文章后希望系統(tǒng)地學(xué)習(xí) mxGraph 還是要去閱讀這些文檔的,現(xiàn)在可以暫時(shí)不看。因?yàn)閯傞_始就堆這么多理論性的東西,對(duì)入門沒有好處。

這篇教程分為兩部分,第一部分結(jié)合我寫的一些例子講解基礎(chǔ)知識(shí)。第二部分則利用第一部分講解的知識(shí)開發(fā)一個(gè)小項(xiàng)目 pokemon-diagram。本教程會(huì)使用到 ES6 語法,而第二部分的項(xiàng)目是用 Vue 寫的。閱讀本教程需要你掌握這兩項(xiàng)預(yù)備知識(shí)。

引入 使用 script 引入

我們來分析一下官方的 HelloWorld 實(shí)例是怎樣通過 script 標(biāo)簽引入 mxGraph 的




    
    Hello World



首先要聲名一個(gè)全局變量 mxBasePath 指向一個(gè)路徑,然后引入 mxGraph。

mxBasePath 指向的路徑作為 mxGraph 的靜態(tài)資源路徑。上圖是 HelloWorld 項(xiàng)目的 mxBasePah,這些資源除了 js 目錄 ,其他目錄下的資源都是 mxGraph 運(yùn)行過程中所需要的,所以要在引入 mxGraph 前先設(shè)置 mxBasePath

再來看看 javascript 目錄下有兩個(gè) mxClient.js 版本。 一個(gè)在 javascript/src/js/mxClient.js ,另一個(gè)在 javascript/mxClient.js,后者是前者打包后的版本,所以兩者是可以替換使用的。如果你的項(xiàng)目是使用 script 標(biāo)簽引入 mxGraph,可以參考我這個(gè)庫(kù)。

模塊化引入

模塊化引入可以參考 pokemon-diagram 的這個(gè)文件 static/mxgraph/index.js

/*** 引入 mxgraph ***/
// src/graph/index.js
import mx from "mxgraph";

const mxgraph = mx({
  mxBasePath: "/static/mxgraph",
});

//fix BUG https://github.com/jgraph/mxgraph/issues/49
window["mxGraph"] = mxgraph.mxGraph;
window["mxGraphModel"] = mxgraph.mxGraphModel;
window["mxEditor"] = mxgraph.mxEditor;
window["mxGeometry"] = mxgraph.mxGeometry;
window["mxDefaultKeyHandler"] = mxgraph.mxDefaultKeyHandler;
window["mxDefaultPopupMenu"] = mxgraph.mxDefaultPopupMenu;
window["mxStylesheet"] = mxgraph.mxStylesheet;
window["mxDefaultToolbar"] = mxgraph.mxDefaultToolbar;

export default mxgraph;


/*** 在其他模塊中使用 ***/
// src/graph/Graph.js
import mxgraph from "./index";

const {
  mxGraph,
  mxVertexHandler,
  mxConstants,
  mxCellState,
  /*......*/
} = mxgraph;

這里有兩點(diǎn)需要注意的

mx 方法傳入的配置項(xiàng) mxBasePath 指向的路徑一定要是一個(gè)可以通過 url 訪問的靜態(tài)資源目錄。舉個(gè)例子,pokemon-diagram 的 static 目錄是個(gè)靜態(tài)資源目錄,該目錄下有 mxgraph/css/common.css 這么個(gè)資源,通過http://localhost:7777 可以訪問 pokemon-diagram 應(yīng)用,那么通過 http://localhost:7777/static/mxgraph/css/common.css 也應(yīng)該是可以訪問 common.css 才對(duì)

如果你是通過 script 標(biāo)簽引入 mxGraph,是不需要綁定全局變量那段代碼的。模塊化引入要使用這段代碼是因?yàn)?,mxGraph 這個(gè)框架有些代碼是通過 window.mxXXX 對(duì)以上屬性進(jìn)行訪問的,如果不做全局綁定使用起來會(huì)有點(diǎn)問題。這是官方一個(gè)未修復(fù)的 BUG,詳情可以查閱上面代碼注釋的 issue

基礎(chǔ)知識(shí)

這部分會(huì)使用到我自己編寫的一些例子。大家可以先把代碼下載下來,這些例子都是不需要使用 node 運(yùn)行的,直接雙擊打開文件在瀏覽器運(yùn)行即可。

Cell

Cell 在 mxGraph 中可以代表組(Group)、節(jié)點(diǎn)(Vertex)、邊(Edge),mxCell 這個(gè)類封裝了 Cell 的操作,本教程不涉及到的內(nèi)容。下文若出現(xiàn) Cell 字眼可以當(dāng)作 節(jié)點(diǎn)。

事務(wù)

官方的 HelloWorld 的例子向我們展示了如何將節(jié)點(diǎn)插入到畫布。比較引人注意的是 beginUpdateendUpdate 這兩個(gè)方法,這兩個(gè)方法在官方例子中出鏡頻率非常高,我們來了解一下他們是干嘛用的,嗯,真是只是了解一下就可以了,因?yàn)楣俜綄?duì)兩個(gè)方法的描述對(duì)入門者來說真的是比較晦澀難懂,而且我在實(shí)際開發(fā)中基本用不上這兩個(gè)方法。可以等掌握這個(gè)框架基本使用后再回過頭來研究。下面的描述來源這個(gè)文檔,我來簡(jiǎn)單概括一下有關(guān)這兩個(gè)方法的相關(guān)信息。

beginUpdate、endUpdate 用于創(chuàng)建一個(gè)事務(wù),一次 beginUpdate 必須對(duì)應(yīng)一次 endUpdate

為了保證,假如 beginUpdate 執(zhí)行失敗,endUpdate 永遠(yuǎn)不會(huì)被調(diào)用,beginUpdate 一定要放到 try 塊之外

為了保證,假如 try 塊內(nèi)更新失敗,endUpdate 也一定被調(diào)用,beginUpdate一定要放到 finally 塊

使用 beginUpdate 與 endUpdate 可提高更新視圖性能,框架內(nèi)部做撤消/重做管理也需要 beginUpdate、endUpdate

按照官方這個(gè)說明,如果我不需要撤消/重做功能,是不是可以不使用這兩個(gè)方法呢。我試著把這兩個(gè)方法從 HelloWorld 例子的代碼中刪掉,結(jié)果程序還是可以正常運(yùn)行。

insertVertex
mxGraph.prototype.insertVertex = function(parent, id, value,
                                          x, y, width, height, style, relative) {

  // 設(shè)置 Cell 尺寸及位置信息
  var geometry = new mxGeometry(x, y, width, height);
  geometry.relative = (relative != null) ? relative : false;

  // 創(chuàng)建一個(gè) Cell
  var vertex = new mxCell(value, geometry, style);
  // ...
  // 標(biāo)識(shí)這個(gè) Cell 是一個(gè)節(jié)點(diǎn)
  vertex.setVertex(true);
  // ...

  // 在畫布上添加這個(gè) Cell
  return this.addCell(vertex, parent);
};

上面是經(jīng)簡(jiǎn)化后的 insertVertex 方法。 insertVertex 做了三件事,先是設(shè)置幾何信息,然后創(chuàng)建一個(gè)節(jié)點(diǎn),最后將這個(gè)節(jié)點(diǎn)添加到畫布。insertEdge 與 insertVertex 類似,中間過程會(huì)調(diào)用 vertex.setEdge(true)Cell 標(biāo)記為邊。從這里我們也可以得知無論節(jié)點(diǎn)還是在 mxGraph 中都是由 mxCell 類表示,只是在該類內(nèi)部標(biāo)識(shí)當(dāng)前 Cell節(jié)點(diǎn) 還是 。

mxGeometry
function mxGeometry(x,y,width,height){}

mxGeometry 類表示 Cell 的幾何信息,寬高比較好理解,只對(duì)節(jié)點(diǎn)有意義,對(duì)邊沒意義。下面通過 02.geometry.html 這個(gè)例子說明如x、y的作用。

mxGeometry 還有一個(gè)很重要的布爾屬性 relative

relativefalse 的節(jié)點(diǎn),表示以畫布左上角為基點(diǎn)進(jìn)行定位,x、y 使用的是絕對(duì)單位

上一小節(jié)提到 insertVertex 內(nèi)部會(huì)創(chuàng)建 mxGeometry 類。使用 mxGraph.insertVertex 會(huì)創(chuàng)建一個(gè) mxGeometry.relative 為 false 的節(jié)點(diǎn),如 A 節(jié)點(diǎn)

relativetrue 的節(jié)點(diǎn),表示以父節(jié)點(diǎn)左上角為基點(diǎn)進(jìn)行定位,x、y 使用的是相對(duì)單位

使用 mxGraph.insertVertex 會(huì)創(chuàng)建一個(gè) relative 為 false 的節(jié)點(diǎn)。如果你要將一個(gè)節(jié)點(diǎn)添加到另一個(gè)節(jié)點(diǎn)中需要在該方法調(diào)用的第9個(gè)參數(shù)傳入 true,將 relative 設(shè)置為 true。這時(shí)子節(jié)點(diǎn)使用相對(duì)坐標(biāo)系,以父節(jié)點(diǎn)左上角作為基點(diǎn),x、y 取值范圍都是 [-1,1]。如 C節(jié)點(diǎn) 相對(duì) B節(jié)點(diǎn)定位。

relativetrue 的邊,x、y 用于定位 label

使用 mxGraph.insertEdge 會(huì)創(chuàng)建一條 relative 為 true 的邊。x、y 用于定位線條上的 label,x 取值范圍是 [-1,1],-1 為起點(diǎn),0 為中點(diǎn),1 為終點(diǎn)。y 表示 label 在邊的正交線上移到的距離。第三個(gè)例子能幫忙大家理解這種情況。

const e1 = graph.insertEdge(parent, null, "30%", v1, v2);
e1.geometry.x = 1;
e1.geometry.y = 100;

設(shè)置樣式

由 03.stylesheet.html 這個(gè)例子我們得知 mxGraph 提供兩種設(shè)置樣式的方式。

第一種是設(shè)置全局樣式。mxStylesheet 類用于管理圖形樣式,通過 graph.getStylesheet() 可以獲取當(dāng)前圖形的 mxStylesheet 對(duì)象。mxStylesheet 對(duì)象的 styles 屬性也是一個(gè)對(duì)象,該對(duì)象默認(rèn)情況下包含兩個(gè)對(duì)象defaultVertexStyle、defaultEdgeStyle,修改這兩個(gè)對(duì)象里的樣式屬性對(duì)所有線條/節(jié)點(diǎn)都生效。

第二種是命名樣式。先創(chuàng)建一個(gè)樣式對(duì)象,然后使用 mxStylesheet.putCellStyle 方法為 mxStylesheet.styles 添加該樣式對(duì)象并命名。在添加 Cell 的時(shí)候,將樣式寫在參數(shù)中。格式如下

[stylename;|key=value;]

分號(hào)前可以跟命名樣式名稱或者一個(gè)樣式的 key、value 對(duì)。

ROUNDED 是一個(gè)內(nèi)置的命名樣式,對(duì)節(jié)點(diǎn)設(shè)置有圓角效果,對(duì)邊設(shè)置則邊的拐彎處為圓角。

例子中設(shè)置折線有一個(gè)需要注意的地方。

// 設(shè)置拖拽邊的過程出現(xiàn)折線,默認(rèn)為直線
graph.connectionHandler.createEdgeState = function () {
  const edge = this.createEdge();
  return new mxCellState(graph.view, edge, graph.getCellStyle(edge));
};

雖然調(diào)用 insertEdge 方法時(shí)已經(jīng)設(shè)置了線條為折線,但是在拖拽邊過程中依然是直線。上面這段代碼重寫了 createEdgeState 方法,將拖動(dòng)中的邊樣式設(shè)置成與靜態(tài)時(shí)的邊樣式一致,都是折線。

查看樣式效果小技巧

mxGraph 所有樣式在這里可以查看,打開網(wǎng)站后可以看到以 STYLE_ 開頭的是樣式常量。但是這些樣式常量并不能展示樣式的效果。下面教大家一個(gè)查看樣式效果的小技巧,使用 draw.io 或 GraphEditor (這兩個(gè)應(yīng)用都是使用 mxGraph 進(jìn)行開發(fā)的) 的 Edit Style 功能可以查看當(dāng)前 Cell 樣式。

比如現(xiàn)在我想將邊的樣式設(shè)置成:折線、虛線、綠色、拐彎為圓角、粗3pt。在 Style 面板手動(dòng)修改樣式后,再點(diǎn)擊 Edit Style 就可以看到對(duì)應(yīng)的樣式代碼。

為了方便觀察我手動(dòng)格式化了樣式,注意最后一行以 entryexit 開頭的樣式代表的是邊出口/入口的靶點(diǎn)坐標(biāo),下一小節(jié)會(huì)進(jìn)行講解。

靶點(diǎn)

關(guān)于如何設(shè)置靶點(diǎn)可以參考 04.anchors.html ,下面也是以這個(gè) Demo 進(jìn)行講解兩個(gè)用戶操作的例子,對(duì)比不同的操作對(duì)于獲取靶點(diǎn)信息的影響。

將鼠標(biāo)懸浮中 A 節(jié)點(diǎn)中心,待節(jié)點(diǎn)高亮?xí)r連接到 B 節(jié)點(diǎn)的一個(gè)靶點(diǎn)上


然后將 A 節(jié)點(diǎn)拖拽到 B 節(jié)點(diǎn)右邊

可以看到如果從圖形中心拖出線條,這時(shí)邊的出口值 exit 為空,只有入口值 entry。如果拖動(dòng)節(jié)點(diǎn) mxGraph 會(huì)智能地調(diào)整線條出口方向。如節(jié)點(diǎn) A 的連接靶點(diǎn)原來是在右邊,節(jié)點(diǎn)拖動(dòng)到節(jié)點(diǎn) B 右邊后靶點(diǎn)也跟著發(fā)生了變化,跑到了左邊,而節(jié)點(diǎn) B 的連接靶點(diǎn)一直沒變。

這次將鼠標(biāo)懸浮到 A 節(jié)點(diǎn)的一個(gè)靶點(diǎn),待靶點(diǎn)高亮?xí)r連接到 B 節(jié)點(diǎn)的一個(gè)靶點(diǎn)上


然后將 A 節(jié)點(diǎn)拖拽到 B 節(jié)點(diǎn)右邊

可以看到這次所有值都有了,連接后拖動(dòng)節(jié)點(diǎn) A,連接靶點(diǎn)的位置也固定不變,mxGraph 不像第一個(gè)例子一樣調(diào)整連接靶點(diǎn)位置。之所以產(chǎn)生這樣的差異是因?yàn)榈谝粋€(gè)例子的邊是從節(jié)點(diǎn)中心拖出的,并沒有出口靶點(diǎn)的信息,而第二個(gè)例子則是明確地從一個(gè)靶點(diǎn)中拖出一條邊。

面向?qū)ο缶幊?/b>

mxGraph 框架是使用面向?qū)ο蟮姆绞竭M(jìn)行編寫的,該框架所有類帶 mx 前綴。在接下來的例子你會(huì)看到很多這種形式的方法重寫(Overwrite)。

const oldBar =  mxFoo.prototype.bar;
mxFoo.prototype.bar = function (...args)=> {
   // .....
    oldBar.apply(this,args);
    // .....
};
節(jié)點(diǎn)組合

這一小節(jié)通過 05.consistuent.html 這個(gè)例子,講解節(jié)點(diǎn)組合需要注意的地方。

組合節(jié)點(diǎn)后默認(rèn)情況下,父節(jié)點(diǎn)是可折疊的,要關(guān)閉折疊功能需要將 foldingEnabled 設(shè)為 false。

graph.foldingEnabled = false;

如果希望在改變父節(jié)點(diǎn)尺寸時(shí),子節(jié)點(diǎn)與父節(jié)點(diǎn)等比例縮放,需要開啟 recursiveResize

graph.recursiveResize = true;

下面是這個(gè)例子最重要的兩段代碼。

/**
 * Redirects start drag to parent.
 */
const getInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent;
mxGraphHandler.prototype.getInitialCellForEvent = function (me) {
  let cell = getInitialCellForEvent.apply(this, arguments);
  if (this.graph.isPart(cell)) {
    cell = this.graph.getModel().getParent(cell);
  }
  return cell;
};

// Redirects selection to parent
graph.selectCellForEvent = function (cell) {
  if (this.isPart(cell)) {
    mxGraph.prototype.selectCellForEvent.call(this, this.model.getParent(cell));
    return;
  }

  mxGraph.prototype.selectCellForEvent.apply(this, arguments);
};

這兩個(gè)方法重寫(Overwrite)了原方法,思路都是判斷如果該節(jié)點(diǎn)是子節(jié)點(diǎn)則替換成父節(jié)點(diǎn)去執(zhí)行剩下的邏輯。

getInitialCellForEvent 在鼠標(biāo)按下(mousedown事件,不是click事件)時(shí)觸發(fā),如果注釋掉這段代碼,不使用父節(jié)點(diǎn)替換,當(dāng)發(fā)生拖拽時(shí)子節(jié)點(diǎn)會(huì)被多帶帶拖拽,不會(huì)與父節(jié)點(diǎn)聯(lián)動(dòng)。使用父節(jié)點(diǎn)替換后,原本子節(jié)點(diǎn)應(yīng)該被拖拽,現(xiàn)在變成了父節(jié)點(diǎn)被拖拽,實(shí)現(xiàn)聯(lián)動(dòng)效果。

selectCellForEvent 其實(shí)是 getInitialCellForEvent 內(nèi)部調(diào)用的一個(gè)方法。這個(gè)方法的作用是將 cell 設(shè)置為 selectionCell,設(shè)置后可通過 mxGraph.getSelectionCell 可獲取得該節(jié)點(diǎn)。與 getInitialCellForEvent 同理,如果不使用父節(jié)點(diǎn)替換,則 mxGraph.getSelectionCell 獲取到的會(huì)是子節(jié)點(diǎn)。項(xiàng)目實(shí)戰(zhàn)我們會(huì)使用到 mxGraph.getSelectionCell 這個(gè)接口。

項(xiàng)目實(shí)戰(zhàn)

這部分我主要挑一些這個(gè)項(xiàng)目比較重要的點(diǎn)進(jìn)行講解。

寫一個(gè)節(jié)點(diǎn)組合

下面以項(xiàng)目的這個(gè)節(jié)點(diǎn)為例,講解如何組合節(jié)點(diǎn)

const insertVertex = (dom) => {
  // ...
  const nodeRootVertex = new mxCell("鼠標(biāo)雙擊輸入", new mxGeometry(0, 0, 100, 135), `node;image=${src}`);
  nodeRootVertex.vertex = true;
  // ...
  
  const title = dom.getAttribute("alt");
  const titleVertex = graph.insertVertex(nodeRootVertex, null, title,
    0.1, 0.65, 80, 16,
    "constituent=1;whiteSpace=wrap;strokeColor=none;fillColor=none;fontColor=#e6a23c",
    true);
  titleVertex.setConnectable(false);

  const normalTypeVertex = graph.insertVertex(nodeRootVertex, null, null,
    0.05, 0.05, 19, 14,
    `normalType;constituent=1;fillColor=none;image=/static/images/normal-type/forest.png`,
    true);
  normalTypeVertex.setConnectable(false);
  // .....
};

單單 nodeRootVertex 就是長(zhǎng)這個(gè)樣子。通過設(shè)置自定義的 node 樣式(見 Graph 類 _putVertexStyle 方法)與 image 屬性設(shè)置圖片路徑配合完成。

因?yàn)槟J(rèn)情況下一個(gè)節(jié)點(diǎn)只能有一個(gè)文本區(qū)和一個(gè)圖片區(qū),要增加額外的文本和圖片就需要組合節(jié)點(diǎn)。在 nodeRootVertex 上加上 titleVertex 文本節(jié)點(diǎn)和 normalTypeVertex 圖片節(jié)點(diǎn),最終達(dá)到這個(gè)效果。

有時(shí)需要為不同子節(jié)點(diǎn)設(shè)置不同的鼠標(biāo)懸浮圖標(biāo),如本項(xiàng)目鼠標(biāo)懸浮到 normalTypeVertex 時(shí)鼠標(biāo)變?yōu)槭中?,參?AppCanvas.vue 的 setCursor 方法,重寫 mxGraph.prototype.getCursorForCell 可以實(shí)現(xiàn)這個(gè)功能。

const setCursor = () => {
  const oldGetCursorForCell = mxGraph.prototype.getCursorForCell;
  graph.getCursorForCell = function (...args) {
    const [cell] = args;
    return cell.style.includes("normalType") ?
      "pointer" :
      oldGetCursorForCell.apply(this, args);
  };
};
編輯內(nèi)容

下面這段代碼是編輯內(nèi)容比較常用的設(shè)置

// 編輯時(shí)按回車鍵不換行,而是完成輸入
this.setEnterStopsCellEditing(true);
// 編輯時(shí)按 escape 后完成輸入
mxCellEditor.prototype.escapeCancelsEditing = false;
// 失焦時(shí)完成輸入
mxCellEditor.prototype.blurEnabled = true;

默認(rèn)情況下輸入內(nèi)容時(shí)如果按回車鍵內(nèi)容會(huì)換行,但有些場(chǎng)景有禁止換行的需求,希望回車后完成輸入,通過graph.setEnterStopsCellEditing(true) 設(shè)置可以滿足需求。

重點(diǎn)說說 mxCellEditor.prototype.blurEnabled 這個(gè)屬性,默認(rèn)情況下如果用戶在輸入內(nèi)容時(shí)鼠標(biāo)點(diǎn)擊了畫布之外的不可聚焦區(qū)域(div、section、article等),節(jié)點(diǎn)內(nèi)的編輯器是不會(huì)失焦的,這導(dǎo)致了 LABEL_CHANGED 事件不會(huì)被觸發(fā)。但在實(shí)際項(xiàng)目開發(fā)中一般我們會(huì)期望,如果用戶在輸入內(nèi)容時(shí)鼠標(biāo)點(diǎn)擊了畫布之外的地方就應(yīng)該算作完成一次輸入,然后通過被觸發(fā)的 LABEL_CHANGED 事件將修改后的內(nèi)容同步到服務(wù)端。通過 mxCellEditor.prototype.blurEnabled = true 這行代碼設(shè)置可以滿足我們的需求。

可換行的 label
const titleVertex = graph.insertVertex(nodeRootVertex, null, title,
      0.1, 0.65, 80, 16,
      "constituent=1;whiteSpace=wrap;strokeColor=none;fillColor=none;fontColor=#e6a23c",
      true);

對(duì)于非輸入的文本內(nèi)容,默認(rèn)情況下即便文本超出容器寬度也是不會(huì)換行的。我們項(xiàng)目中寬度為 80 的 titleVertex 正是這樣一個(gè)例子。

要設(shè)置換行需要做兩件事,第一是通過這行代碼 mxGraph.setHtmlLabels(true),使用 html 渲染文本(mxGraph 默認(rèn)使用 svg的text 標(biāo)簽渲染文本)。第二是像上面的 titleVertex 的樣式設(shè)置一樣,添加一句 whiteSpace=wrap。

Model

現(xiàn)在介紹一下 Model 這個(gè)概念,Model 是當(dāng)前圖形的數(shù)據(jù)結(jié)構(gòu)化表示。mxGraphModel 封裝了 Model 的相關(guān)操作。

你可以啟動(dòng)項(xiàng)目,畫一個(gè)這樣的圖,然后點(diǎn)擊輸出XML。為了保的 xml 與下面的一致,需要先拖出智爺,再拖出超級(jí)皮卡丘,最后連接邊。

控制臺(tái)應(yīng)該輸出這樣一份 xml


  
    
    
    
      
    
    ........
  

每一個(gè) mxCell 節(jié)點(diǎn)都有 parent 屬性指向父節(jié)點(diǎn)。我們對(duì) value="Hello" 這個(gè) mxCell 節(jié)點(diǎn)手動(dòng)格式化。


  

data 值是原對(duì)象經(jīng) JSON.stringify 得到的,經(jīng)轉(zhuǎn)義后就變成了上面的樣子??刂婆_(tái)還打印了一個(gè) mxGraphModel 對(duì)象,對(duì)比上面的 xml 與 下圖的節(jié)點(diǎn)對(duì)象,可以發(fā)現(xiàn)它們只是同一個(gè) Model 的不同表現(xiàn)形式,xml 正是將 mxGraph.model 格式化而成的。

事件

本項(xiàng)目監(jiān)聽事件寫在 AppCanvas.vue 的 _listenEvent 方法,可以在這個(gè)方法了解一些常用的事件。下圖來自 mxGraph 類的方法調(diào)用依賴圖,我們可以從這里看出整個(gè)框架的事件流動(dòng)。

監(jiān)聽事件

本項(xiàng)目的 _listenEvent 方法用到兩個(gè)事件監(jiān)聽對(duì)象。

mxGraph 繼承自 mxEventSource,使用父類的 addListener 方法可以將自身當(dāng)作一個(gè)事件中心進(jìn)行訂閱/廣播事件。

mxGraph.getSelectionModel() 返回一個(gè) mxGraphSelectionModel 對(duì)象,這個(gè)對(duì)象也是繼承自 mxEventSourcemxEvent.UNDO、mxEvent.CHANGE 兩個(gè)事件,通過監(jiān)聽 mxEvent.CHANGE 事件可以獲取當(dāng)前被選中的 Cell。

ADD_CELLS 與 CELLS_ADD 的區(qū)別

mxGraph 類有很多 XXX_CELLSCELLS_XXXED 這種形式的事件,這部分我還沒弄懂,下面僅以添加事件為例探討這兩類事件的區(qū)別。

添加 Cell 的時(shí)候會(huì)觸發(fā)兩個(gè)事件 ADD_CELLS、CELLS_ADDED, 先觸發(fā) CELLS_ADDED 后觸發(fā) ADD_CELLS

ADD_CELLSaddCells 方法中觸發(fā),而 CELLS_ADDEDcellsAdded 方法中觸發(fā)。而對(duì)于 addCells 與 cellsAdded 官方文檔的說明并不能體現(xiàn)出兩者的區(qū)別,再深究下去就要查閱源碼了。按經(jīng)驗(yàn)而言后觸發(fā)的事件會(huì)攜帶更多的信息,所以平時(shí)開發(fā)我會(huì)監(jiān)聽 ADD_CELLS 事件。MOVE_CELLS、CELLS_MOVED、REMOVE_CELLS、CELLS_REMOVED 等事件與此類似。

監(jiān)聽 Cell 添加事件

從上面的方法調(diào)用依賴圖中我們可以看到,insertVertex、insertEdge 最終都被當(dāng)作 Cell 處理,在后續(xù)觸發(fā)的事件也沒有對(duì) 節(jié)點(diǎn)/邊 進(jìn)行區(qū)分,而是統(tǒng)一當(dāng)作 Cell 事件。所以對(duì)于一個(gè) Cell 添加事件,需要自己區(qū)別是添加了節(jié)點(diǎn)還是添加了邊。

graph.addListener(mxEvent.CELLS_ADDED, (sender, evt) => {
  const cell = evt.properties.cells[0];
  if (graph.isPart(cell)) {
    return;
  }

  if (cell.vertex) {
    this.$message.info("添加了一個(gè)節(jié)點(diǎn)");
  } else if (cell.edge) {
    this.$message.info("添加了一條線");
  }
});

還有就是對(duì)于子節(jié)點(diǎn)添加到父節(jié)點(diǎn)的情況(如本項(xiàng)目將 titleVertex 、normalTypeVertex 添加到 nodeRootVertex)也是會(huì)觸發(fā) Cell 添加事件的。通常對(duì)于這些子節(jié)點(diǎn)不作處理,可以像 05.consistuent.html 一樣用一個(gè) isPart 判斷過濾掉。

自定義事件

上面提到過 mxGraph 繼承自 mxEventSource,調(diào)用父類的 fireEvent 可觸發(fā)自定義事件。下面是一個(gè)簡(jiǎn)單的例子

mxGraph.addListener("自定義事件A",()=>{ 
  // do something .....
});
// 觸發(fā)自定義事件
mxGraph.fireEvent(new mxEventObject("自定義事件A");

在本項(xiàng)目 Graph 類的 _configCustomEvent 方法我也實(shí)現(xiàn)了兩個(gè)自定義事件。當(dāng)邊開始拖動(dòng)時(shí)會(huì)觸發(fā) EDGE_START_MOVE 事件,當(dāng)節(jié)點(diǎn)開始拖動(dòng)時(shí)會(huì)觸發(fā) VERTEX_START_MOVE 事件。

導(dǎo)出圖片

mxGraph 導(dǎo)出圖片的思路是先在前端導(dǎo)出圖形的 xml 及計(jì)算圖形的寬高,然后將 xml、寬、高,這有三項(xiàng)數(shù)據(jù)發(fā)送給服務(wù)端,服務(wù)端也使用 mxGraph 提供的 API 將 xml 轉(zhuǎn)換成圖片。服務(wù)端如果是使用 Java 可以參考官方這個(gè)例子,下面主要介紹前端需要做的工作。

導(dǎo)出圖片可以使用 mxImageExport 類,該類的文檔有一段可以直接拿來使用的代碼。

// ...
var xmlCanvas = new mxXmlCanvas2D(root);
var imgExport = new mxImageExport();
imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas);

var bounds = graph.getGraphBounds();
var w = Math.ceil(bounds.x + bounds.width);
var h = Math.ceil(bounds.y + bounds.height);

var xml = mxUtils.getXml(root);
// ...

但這段代碼會(huì)將整塊畫布截圖,而不是以最左上角的元素及最右下角的元素作為邊界截圖。如果你有以元素作為邊界的需求,則需要調(diào)用 xmlCanvas.translate 調(diào)整裁圖邊界。

//.....
var xmlCanvas = new mxXmlCanvas2D(root);
xmlCanvas.translate(
      Math.floor((border / scale - bounds.x) / scale),
      Math.floor((border / scale - bounds.y) / scale),
    );
//.....

完整截圖代碼可以參考本項(xiàng)目 Graph 類的 exportPicXML 方法。

如果節(jié)點(diǎn)像我的項(xiàng)目一樣使用到圖片,而導(dǎo)出來的圖片的節(jié)點(diǎn)沒有圖片??梢詮膬蓚€(gè)方向排查問題,先檢查發(fā)送的 xml 里的圖片路徑是否是可訪問的,如下面是項(xiàng)目“導(dǎo)出圖片”功能打印的 xml 里的一個(gè)圖片標(biāo)簽。

要保證 http://localhost:7777/static/images/ele/ele-005.png 是可訪問的。如果圖片路徑?jīng)]問題再檢查一下使用的圖片格式,本來我在公司項(xiàng)目中節(jié)點(diǎn)內(nèi)使用的圖片是 svg 格式,導(dǎo)出圖片失敗,可能是 mxGraph 不支持這個(gè)格式,后來換成 png 之后問題就解決了。

還有就是如果導(dǎo)出的圖片里的節(jié)點(diǎn)的某些顏色跟設(shè)置的有差異,那可能是設(shè)置樣式時(shí)寫了3位數(shù)的顏色像 #fff,顏色一定要使用完整的6位,否則導(dǎo)出圖片會(huì)有問題。

參考

mxGraph Tutorial

mxGraph User Manual – JavaScript Client

mxGraph API Specification

mxGraph Javascript Examples

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102530.html

相關(guān)文章

  • mxGraph使用經(jīng)驗(yàn)總結(jié)

    摘要:邊繪制成折線的時(shí)候?yàn)閮煞N形式,默認(rèn)是通過貝塞爾曲線繪制成帶圓角的折線,另一種是直角折線。在開發(fā)中我對(duì)邊的繪制方式進(jìn)行了小小的修改,統(tǒng)一改為直接使用三次貝塞爾曲線連接,具體代碼如下調(diào)用內(nèi)置函數(shù)繪制三次貝塞爾曲線忽略后面繪制折線的代碼參考 mxGraph是一個(gè)支持多種語言(Java、JavaScript、PHP、.NET)的畫圖框架,所繪制的圖形可以在主流瀏覽器以及原生應(yīng)用上使用。mxGr...

    k00baa 評(píng)論0 收藏0
  • mxGraph使用經(jīng)驗(yàn)總結(jié)

    摘要:首先不支持使用,。相關(guān)代碼如下邊的邊都是自動(dòng)繪制的,支持對(duì)邊的樣式修改,比如箭頭粗細(xì)等。邊繪制成折線的時(shí)候?yàn)閮煞N形式,默認(rèn)是通過貝塞爾曲線繪制成帶圓角的折線,另一種是直角折線。mxGraph是一個(gè)支持多種語言(Java、JavaScript、PHP、.NET)的畫圖框架,所繪制的圖形可以在主流瀏覽器以及原生應(yīng)用上使用。 mxGraph官方資料全英文,網(wǎng)上有幾篇mxGraph的教程,對(duì)于入門和...

    raledong 評(píng)論0 收藏0
  • 記一次繪圖框架技術(shù)選型: jsPlumb VS mxGraph

    摘要:公司項(xiàng)目需要用到繪圖框架,繪圖部分以前是另一位同事負(fù)責(zé),用的是框架?;谝陨咸峒暗降姆N種原因,上年年末我做起了技術(shù)調(diào)研,希望能找到一個(gè)合適我們項(xiàng)目的繪圖框架。兼容性問題項(xiàng)目對(duì)瀏覽器兼容性比較寬松,瀏覽器兼容性問題不在考慮范圍之內(nèi)。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1g0ppk2kkhxj30ka0b4gm5.jpg); 公司...

    longmon 評(píng)論0 收藏0
  • 記一次繪圖框架技術(shù)選型: jsPlumb VS mxGraph

    摘要:公司項(xiàng)目需要用到繪圖框架,繪圖部分以前是另一位同事負(fù)責(zé),用的是框架?;谝陨咸峒暗降姆N種原因,上年年末我做起了技術(shù)調(diào)研,希望能找到一個(gè)合適我們項(xiàng)目的繪圖框架。兼容性問題項(xiàng)目對(duì)瀏覽器兼容性比較寬松,瀏覽器兼容性問題不在考慮范圍之內(nèi)。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1g0ppk2kkhxj30ka0b4gm5.jpg); 公司...

    channg 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<