MxGraph使用心得(2019-03-25)


最近根據(jù)公司需求接觸了mxGraph技術(shù),mxGraph 是一個(gè) JS 繪圖組件適用于需要在網(wǎng)頁(yè)中設(shè)計(jì)/編輯 Workflow/BPM 流程圖、圖表、網(wǎng)絡(luò)圖和普通圖形的 Web 應(yīng)用程序框架。

官方文檔:http://jgraph.github.io/mxgraph/javascript/index.html
官方API:http://jgraph.github.io/mxgraph/docs/js-api
GitHub庫(kù):jgraph/mxgraph

一耙考、下載:使用前首先要下載庫(kù)直接在github上拉取項(xiàng)目,以下是項(xiàng)目目錄鲸沮。
屏幕快照 2019-03-28 下午4.56.45.png
javaScript文件夾下的mxClient.js就是我們要用的文件玖详,mxClient.min.js是壓縮后的代碼啦吧,用于打包上線時(shí)引用糠爬。
二叼耙、配置&&引入:
<script type="text/javascript">
    mxBasePath = '../src';
</script>
<script type="text/javascript" src="../src/js/mxClient.js"></script>

要定義一個(gè)mxBasePath為存放靜態(tài)資源的路徑。
以<script>標(biāo)簽引入怒坯。

在Vue中引入
npm install mxgraph --save
npm install exports-loader --save
npm install script-loader --save
在項(xiàng)目根目錄新建vue.config.js文件炫狱。配置如下:
const path = require('path');

function resolve(dir) {
    return path.join(__dirname, dir);
}

module.exports = {
    publicPath: './',
    outputDir: 'dist',
    lintOnSave: true,
    chainWebpack: (config) => {
        config.module
            .rule('')
            .test(/mxClient\.js$/)
            .use('exports-loader')
            .loader('exports-loader?mxClient,mxGraphModel,mxActor,mxShape,mxEventObject,mxGraph,mxPrintPreview,mxEventSource,mxRectangle,mxVertexHandler,mxMouseEvent,mxGraphView,mxImage,mxGeometry,mxRubberband,mxKeyHandler,mxDragSource,mxGraphModel,mxEvent,mxUtils,mxWindow,mxEvent,mxCodec,mxCell,mxConstants,mxPoint,mxGraphHandler,mxCylinder,mxCellRenderer,mxEvent,mxUndoManager')
            .end();
        config.resolve.alias
            .set('@', resolve('src'))
            .set('@assets', resolve('src/assets'));
        // 按這種格式.set('', resolve('')) 自己添加
    }
};

--------------------- 
作者:懶牛不愛(ài)梳毛 
來(lái)源:CSDN 
原文:https://blog.csdn.net/dikentoujing99/article/details/86630652 
.Vue
<template>
    <div ref="graph_container"></div>
</template>

<script>
import {
    mxGraph
} from 'mxgraph/javascript/mxClient';

export default {
    name: 'HelloWorld',
    props: {
        msg: String
    },
    mounted() {
        // Creates the graph inside the given container
        var graph = new mxGraph(this.$refs.graph_container);

        // Gets the default parent for inserting new cells. This
        // is normally the first child of the root (ie. layer 0).
        var parent = graph.getDefaultParent();

        // Adds cells to the model in a single step
        graph.getModel().beginUpdate();
        try {
            let v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
            let v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);

            graph.insertEdge(parent, null, '', v1, v2);
        } finally {
            // Updates the display
            graph.getModel().endUpdate();
        }
    }
};
</script>

使用到mxGraph的哪個(gè)方法就得把它們import進(jìn)來(lái)。

三剔猿、具體操作

1.mxGraph中有三個(gè)主要的組件:mxGraph视译、mxGraphModel、mxCell归敬。mxGraph是用戶(hù)直接操作的圖酷含,圖的所有狀態(tài)都保存在mxGraphModel中,而圖中的頂點(diǎn)和邊都是用mxCell定義汪茧。
2.當(dāng)用戶(hù)對(duì)mxGraph進(jìn)行操作時(shí)椅亚,所有操作都映射到對(duì)mxGraphModel中保存的狀態(tài)進(jìn)行修改,而mxGraphModel中保存的狀態(tài)也就是mxCell的狀態(tài)舱污。

舉個(gè)最簡(jiǎn)單的??

<html>
<head>
    <title>Hello, World! example for mxGraph</title>
    <!-- Sets the basepath for the library if not in same directory -->
    <script type="text/javascript">
        mxBasePath = '../src';
    </script>
    <!-- Loads and initializes the library -->
    <script type="text/javascript" src="../src/js/mxClient.js"></script>
    <!-- Example code -->
    <script type="text/javascript">
        
        function main(container)
        {
            // 判斷瀏覽器是否支持
            if (!mxClient.isBrowserSupported())
            {
                // mxUtils報(bào)錯(cuò)提示
                mxUtils.error('Browser is not supported!', 200, false);
            }
            else
            {
                //去鋸齒效果
                mxRectangleShape.prototype.crisp = true;
                // 顯示導(dǎo)航線 
                mxGraphHandler.prototype.guidesEnabled = true;
                // Alt鍵禁用導(dǎo)航線
                mxGuide.prototype.isEnabledForEvent = function (evt) {
                  return !mxEvent.isAltDown(evt);
                };
                // 顯示終點(diǎn)
                mxEdgeHandler.prototype.snapToTerminals = false;
                // 禁用瀏覽器默認(rèn)的右鍵菜單欄
                mxEvent.disableContextMenu(container);

                // 在已有容器內(nèi)構(gòu)造一個(gè)graph
                var graph = new mxGraph(container);
                // 鼠標(biāo)框選 
                new mxRubberband(graph);
                // 在圖形中創(chuàng)建默認(rèn)組件 
                var parent = graph.getDefaultParent();
                // 只可預(yù)覽不可選中拖動(dòng)連接
                graph.setEnabled(false);  
                // 容器大小自適應(yīng) 
                graph.setResizeContainer(true); 
                // 動(dòng)態(tài)改變樣式 
                graph.getView().updateStyle = true; 
                // 可否重復(fù)連接 
                graph.setMultigraph(false); 
                // 禁止改變?cè)卮笮?
                graph.setCellsResizable(false); 
                // 允許連線的目標(biāo)和源是同一元素 
                graph.setAllowLoops(true); 

                // 開(kāi)始往module里添加cell
                graph.getModel().beginUpdate();
                try
                {
                    //new一個(gè)cell   以單元的形式創(chuàng)建一個(gè)節(jié)點(diǎn)
                    var cell = new mxCell(null, new mxGeometry(100, 200, 100, 100), "一些樣式配置");     
                    cell.vertex = true;
                    //插入這個(gè)cell
                    graph.addCell(cell);
                 
                    var v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
                    var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
                    //插入線條設(shè)置連接圖形
                    var e1 = graph.insertEdge(parent, null, '', v1, v2);

                    //預(yù)覽時(shí)鼠標(biāo)懸浮到節(jié)點(diǎn)時(shí)呀舔,改變鼠標(biāo)樣式
                    graph.getCursorForCell = function (cell) {
                      if (cell != null && cell.value != null && cell.vertex == 1) {
                         return 'pointer';
                      }
                    };
                }
                finally
                {
                    // 更新事務(wù)結(jié)束
                    graph.getModel().endUpdate();
                }
            }
        };
    </script>
</head>

<!-- Page passes the container for the graph to the program -->
<body onload="main(document.getElementById('graphContainer'))">

    <!-- Creates a container for the graph with a grid wallpaper -->
    <div id="graphContainer"
        style="position:relative;overflow:hidden;width:321px;height:241px;background:url('editors/images/grid.gif');cursor:default;">
    </div>
</body>
</html>

1.事物的更新一定要放在 beginUpdate 和 endUpdate 里面。一次 beginUpdate 必須對(duì)應(yīng)一次 endUpdate
2.插入cell有兩種方式:

new mxCell(null, new mxGeometry(100, 200, 100, 100))
graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30)
insertVertex 做了三件事扩灯,先是設(shè)置幾何信息媚赖,然后創(chuàng)建一個(gè)節(jié)點(diǎn)霜瘪,最后將這個(gè)節(jié)點(diǎn)添加到畫(huà)布。insertEdge 與 insertVertex 類(lèi)似惧磺,中間過(guò)程會(huì)調(diào)用 vertex.setEdge(true)Cell 標(biāo)記為邊颖对。
幾何信息四個(gè)數(shù)字分別對(duì)應(yīng) X、 Y磨隘、 寬惜互、 高 坐標(biāo)是以graph的左上角為原點(diǎn)。

3.最上面是針對(duì)圖區(qū)域的一些設(shè)置
4.對(duì)插入元素的樣式配置跟在mxCell()最后面的參數(shù)里琳拭,樣式可以有很多,庫(kù)里也提供了一些描验。就不多說(shuō)白嘁,說(shuō)一下自定義樣式。
          var style1 = [];
          style1[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
          style1[mxConstants.STYLE_IMAGE] = './demoimg/屏幕快照 2019-03-27 下午1.06.15.png';
          style1[mxConstants.STYLE_IMAGE_WIDTH] = '48';
          style1[mxConstants.STYLE_IMAGE_HEIGHT] = '48';
          graph.getStylesheet().putCellStyle('img1', style1);
          //插入節(jié)點(diǎn)時(shí)定義樣式
          var cell1 = new mxCell(null, new mxGeometry(100, 200, 100, 100), "img1");
5.添加按鈕

document.body.appendChild(mxUtils.button('value', function(evt){}

分裝一個(gè)動(dòng)態(tài)添加不同功能的按鈕的方法

        // 創(chuàng)建按鈕
  createButton = function (label, fun) {
    document.getElementById("btn").appendChild(mxUtils.button(label, fun));
  };

  buttons = [{
        label: "選擇所有",
        fun: function (graph) {
          return function (evt) {
            graph.selectAll();//graph提供了很多的不同方法的API
          };
        }
      },
      {
         label: "刪除",
         fun: function (graph) {
           return function (evt) {
             var cells = graph.getSelectionCells();
             graph.removeCells(cells);
           };
         }
       },
     ]

//循環(huán)添加所有設(shè)置好功能的按鈕
(function () {
  for (var i = 0; i < buttons.length; i++) {
    createButton(buttons[i].label, buttons[i].fun(graph));
  }
})();
6.讀取Xml
var xml ="<root><mxCell id='2' value='Hello,' vertex='1'><mxGeometry x='20' y='20' width='80' height='30' as='geometry'/></mxCell><mxCell id='3' value='World!' vertex='1'><mxGeometry x='200' y='150' width='80' height='30' as='geometry'/></mxCell><mxCell id='4' value='' edge='1' source='2' target='3'><mxGeometry relative='1' as='geometry'/></mxCell></root>";
var doc = mxUtils.parseXml(xml);
var codec = new mxCodec(doc);
var elt = doc.documentElement.firstChild;
var cells = [];
while (elt != null) {
  cells.push(codec.decode(elt));
  elt = elt.nextSibling;
}
graph.addCells(cells);

7.建立拖拽關(guān)系(選取拖動(dòng)定點(diǎn)添加)

// 檢查圖形中是否包含對(duì)應(yīng)的elt節(jié)點(diǎn)
        function containsElt(graph, elt) {
          while (elt != null) {
            if (elt == graph.container) {
              return true;
            }

            elt = elt.parentNode;
          }

          return false;
        };

        // 返回鼠標(biāo)選中的元素
        var graphF = function (evt) {
          var x = mxEvent.getClientX(evt);
          var y = mxEvent.getClientY(evt);
          var elt = document.elementFromPoint(x, y);

          for (var i = 0; i < graphs.length; i++) {
            if (containsElt(graphs[i], elt)) {
              return graphs[i];
            }
          }

          return null;
        };

        // 在給定的位置插入一個(gè)元素 
        //mxCell是頂點(diǎn)和邊的單元對(duì)象膘流,mxCell從模型(mxGraph)那里復(fù)制了許多的方法
        //它們的主要差別在于絮缅,使用模型的方法會(huì)創(chuàng)建相關(guān)的事件通知以及撤銷(xiāo)方法,使用單元的方法可以發(fā)生改變但不記錄它們
        var funct = function (graph, evt, target, x, y) {
          var cell = new mxCell('NewCELL', new mxGeometry(0, 0, 120, 40));
          cell.vertex = true;
          var cells = graph.importCells([cell], x, y, target); //插入元素呼股、位置耕魄、大小

          if (cells != null && cells > 0) {
            graph.scrollCellToVisible(cells[0]);
            graph.setSelectionCells(cells);
          }
        };

        //創(chuàng)建一個(gè)DOM節(jié)點(diǎn),作為拖動(dòng)源
        var img = mxUtils.createImage('images/icons48/gear.png');
        img.style.width = '48px';
        img.style.height = '48px';
        img.style.margin = '10px';
        document.getElementById("left").appendChild(img);

        // 禁用IE瀏覽器中的DnD功能(這是為了跨瀏覽器平臺(tái)而設(shè)計(jì)的彭谁,見(jiàn)下文) 
        if (mxClient.IS_IE) {
          mxEvent.addListener(img, 'dragstart', function (evt) {
            evt.returnValue = false;
          });
        }

        // 創(chuàng)建拖動(dòng)源的預(yù)覽
        var dragElt = document.createElement('div');
        dragElt.style.border = 'dashed black 1px';
        dragElt.style.width = '120px';
        dragElt.style.height = '40px';

        // 在點(diǎn)擊拖動(dòng)源圖標(biāo)時(shí)提供預(yù)覽吸奴。 預(yù)覽是提供的僅僅是拖動(dòng)源的圖片
        // 只有拖動(dòng)源到容器內(nèi)時(shí)才會(huì)顯示元素的坐標(biāo)預(yù)覽

        var ds = mxUtils.makeDraggable(img, graphF, funct, dragElt, null, null, graph.autoscroll, true);

        //從拖動(dòng)源拖動(dòng)時(shí)顯示導(dǎo)航線。
        //注意缠局,對(duì)圖形中已存在的元素拖動(dòng)時(shí)顯示導(dǎo)航線不在本方法約束范圍则奥。
        ds.isGuidesEnabled = function () {
          return graph.graphHandler.guidesEnabled;
        };

        //從拖動(dòng)源拖動(dòng)元素到圖形以外的區(qū)域時(shí),顯示拖動(dòng)源圖片預(yù)覽
        ds.createDragElement = mxDragSource.prototype.createDragElement;

var ds = mxUtils.makeDraggable(img, graphF, funct, dragElt, null, null, graph.autoscroll, true);
makeDraggable的參數(shù)makeDraggable( element, graphF, funct, dragElement, dx, dy, autoscroll, scalePreview, highlightDropTargets, getDropTarget)
詳細(xì)信息請(qǐng)點(diǎn)這里


前人栽樹(shù)狭园,后人乘涼

關(guān)于mxGraph读处,官方API文檔不友好,介紹簡(jiǎn)單不說(shuō)還全是英文的唱矛,官網(wǎng)的Demo倒是不少罚舱,不過(guò)有的拉下來(lái)在自己的環(huán)境下跑會(huì)報(bào)錯(cuò),同時(shí)國(guó)內(nèi)對(duì)于mxGraph技術(shù)的文章真的是少之又少绎谦,學(xué)習(xí)過(guò)程簡(jiǎn)直就是摸著石頭過(guò)河管闷,目前只基本掌握基礎(chǔ)使用部分,文章還會(huì)繼續(xù)更新燥滑。希望可以幫助到有需要的朋友渐北。

如果對(duì)你有幫助請(qǐng)幫我點(diǎn)個(gè) 喜歡~~~撒花??
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铭拧,隨后出現(xiàn)的幾起案子赃蛛,更是在濱河造成了極大的恐慌恃锉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呕臂,死亡現(xiàn)場(chǎng)離奇詭異破托,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)歧蒋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)土砂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人谜洽,你說(shuō)我怎么就攤上這事萝映。” “怎么了阐虚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵序臂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我实束,道長(zhǎng)奥秆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任咸灿,我火速辦了婚禮构订,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘避矢。我一直安慰自己悼瘾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布谷异。 她就那樣靜靜地躺著分尸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歹嘹。 梳的紋絲不亂的頭發(fā)上箩绍,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音尺上,去河邊找鬼材蛛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛怎抛,可吹牛的內(nèi)容都是我干的卑吭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼马绝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼豆赏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掷邦,失蹤者是張志新(化名)和其女友劉穎白胀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體抚岗,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡或杠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宣蔚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片向抢。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胚委,靈堂內(nèi)的尸體忽然破棺而出挟鸠,到底是詐尸還是另有隱情,我是刑警寧澤亩冬,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布兄猩,位于F島的核電站,受9級(jí)特大地震影響鉴未,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸠姨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一铜秆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讶迁,春花似錦连茧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祟峦,卻和暖如春罚斗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宅楞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工针姿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厌衙。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓距淫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親婶希。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榕暇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評(píng)論 4 62
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,089評(píng)論 1 32
  • 今天是2017年7月1日彤枢,一個(gè)特殊而值得紀(jì)念的日子狰晚。今天是我的生日,今天我就24歲了堂污,24年前的今天家肯,我的母親忍...
    日光傾城_625c閱讀 581評(píng)論 2 0
  • 又一年匆匆忙忙地結(jié)束了,匆忙得來(lái)不及數(shù)一下我們的腳步盟猖,匆忙得來(lái)不及聽(tīng)一下我們的足音讨衣。我們開(kāi)始檢點(diǎn)自己的日子,發(fā)現(xiàn)歲...
    mice411閱讀 725評(píng)論 3 4