国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

mxGraph 入門實例教程

coordinate35 / 2735人閱讀

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

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

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

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

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

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

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

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

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

在線實例,這些實例的源碼都在這里有

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

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

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

引入 使用 script 引入

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




    
    Hello World



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

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

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

模塊化引入

模塊化引入可以參考 pokemon-diagram 的這個文件 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 方法傳入的配置項 mxBasePath 指向的路徑一定要是一個可以通過 url 訪問的靜態(tài)資源目錄。舉個例子,pokemon-diagram 的 static 目錄是個靜態(tài)資源目錄,該目錄下有 mxgraph/css/common.css 這么個資源,通過http://localhost:7777 可以訪問 pokemon-diagram 應(yīng)用,那么通過 http://localhost:7777/static/mxgraph/css/common.css 也應(yīng)該是可以訪問 common.css 才對

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

基礎(chǔ)知識

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

Cell

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

事務(wù)

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

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

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

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

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

按照官方這個說明,如果我不需要撤消/重做功能,是不是可以不使用這兩個方法呢。我試著把這兩個方法從 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)建一個 Cell
  var vertex = new mxCell(value, geometry, style);
  // ...
  // 標(biāo)識這個 Cell 是一個節(jié)點(diǎn)
  vertex.setVertex(true);
  // ...

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

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

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

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

mxGeometry 還有一個很重要的布爾屬性 relative

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

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

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

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

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

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

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

設(shè)置樣式

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

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

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

[stylename;|key=value;]

分號前可以跟命名樣式名稱或者一個樣式的 key、value 對。

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

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

// 設(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 方法時已經(jīng)設(shè)置了線條為折線,但是在拖拽邊過程中依然是直線。上面這段代碼重寫了 createEdgeState 方法,將拖動中的邊樣式設(shè)置成與靜態(tài)時的邊樣式一致,都是折線。

查看樣式效果小技巧

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

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

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

靶點(diǎn)

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

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


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

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

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


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

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

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

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

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

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

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

graph.foldingEnabled = false;

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

graph.recursiveResize = true;

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

/**
 * 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);
};

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

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

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

項目實戰(zhàn)

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

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

下面以項目的這個節(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 就是長這個樣子。通過設(shè)置自定義的 node 樣式(見 Graph 類 _putVertexStyle 方法)與 image 屬性設(shè)置圖片路徑配合完成。

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

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

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è)置

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

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

重點(diǎn)說說 mxCellEditor.prototype.blurEnabled 這個屬性,默認(rèn)情況下如果用戶在輸入內(nèi)容時鼠標(biāo)點(diǎn)擊了畫布之外的不可聚焦區(qū)域(div、section、article等),節(jié)點(diǎn)內(nèi)的編輯器是不會失焦的,這導(dǎo)致了 LABEL_CHANGED 事件不會被觸發(fā)。但在實際項目開發(fā)中一般我們會期望,如果用戶在輸入內(nèi)容時鼠標(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);

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

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

Model

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

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

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


  
    
    
    
      
    
    ........
  

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


  

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

事件

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

監(jiān)聽事件

本項目的 _listenEvent 方法用到兩個事件監(jiān)聽對象。

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

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

ADD_CELLS 與 CELLS_ADD 的區(qū)別

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

添加 Cell 的時候會觸發(fā)兩個事件 ADD_CELLSCELLS_ADDED, 先觸發(fā) CELLS_ADDED 后觸發(fā) ADD_CELLS

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

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

從上面的方法調(diào)用依賴圖中我們可以看到,insertVertexinsertEdge 最終都被當(dāng)作 Cell 處理,在后續(xù)觸發(fā)的事件也沒有對 節(jié)點(diǎn)/邊 進(jìn)行區(qū)分,而是統(tǒng)一當(dāng)作 Cell 事件。所以對于一個 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("添加了一個節(jié)點(diǎn)");
  } else if (cell.edge) {
    this.$message.info("添加了一條線");
  }
});

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

自定義事件

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

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

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

導(dǎo)出圖片

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

導(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);
// ...

但這段代碼會將整塊畫布截圖,而不是以最左上角的元素及最右下角的元素作為邊界截圖。如果你有以元素作為邊界的需求,則需要調(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),
    );
//.....

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

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

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

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

參考

mxGraph Tutorial

mxGraph User Manual – JavaScript Client

mxGraph API Specification

mxGraph Javascript Examples

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/102530.html

相關(guān)文章

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

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

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

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

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

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

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

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

    channg 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<