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


最近根据公司需求接触了mxGraph技术,mxGraph 是一个 JS 绘图组件适用于需要在网页中设计/编辑 Workflow/BPM 流程图、图表、网络图和普通图形的 Web 应用程序框架。

官方文档:http://jgraph.github.io/mxgraph/javascript/index.html
官方API:http://jgraph.github.io/mxgraph/docs/js-api
GitHub库:jgraph/mxgraph

一、下载:使用前首先要下载库直接在github上拉取项目,以下是项目目录。
屏幕快照 2019-03-28 下午4.56.45.png
javaScript文件夹下的mxClient.js就是我们要用的文件,mxClient.min.js是压缩后的代码,用于打包上线时引用。
二、配置&&引入:
<script type="text/javascript">
    mxBasePath = '../src';
</script>
<script type="text/javascript" src="../src/js/mxClient.js"></script>

要定义一个mxBasePath为存放静态资源的路径。
以<script>标签引入。

在Vue中引入
npm install mxgraph --save
npm install exports-loader --save
npm install script-loader --save
在项目根目录新建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('')) 自己添加
    }
};

--------------------- 
作者:懒牛不爱梳毛 
来源: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的哪个方法就得把它们import进来。

三、具体操作

1.mxGraph中有三个主要的组件:mxGraph、mxGraphModel、mxCell。mxGraph是用户直接操作的图,图的所有状态都保存在mxGraphModel中,而图中的顶点和边都是用mxCell定义。
2.当用户对mxGraph进行操作时,所有操作都映射到对mxGraphModel中保存的状态进行修改,而mxGraphModel中保存的状态也就是mxCell的状态。

举个最简单的🌰

<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报错提示
                mxUtils.error('Browser is not supported!', 200, false);
            }
            else
            {
                //去锯齿效果
                mxRectangleShape.prototype.crisp = true;
                // 显示导航线 
                mxGraphHandler.prototype.guidesEnabled = true;
                // Alt键禁用导航线
                mxGuide.prototype.isEnabledForEvent = function (evt) {
                  return !mxEvent.isAltDown(evt);
                };
                // 显示终点
                mxEdgeHandler.prototype.snapToTerminals = false;
                // 禁用浏览器默认的右键菜单栏
                mxEvent.disableContextMenu(container);

                // 在已有容器内构造一个graph
                var graph = new mxGraph(container);
                // 鼠标框选 
                new mxRubberband(graph);
                // 在图形中创建默认组件 
                var parent = graph.getDefaultParent();
                // 只可预览不可选中拖动连接
                graph.setEnabled(false);  
                // 容器大小自适应 
                graph.setResizeContainer(true); 
                // 动态改变样式 
                graph.getView().updateStyle = true; 
                // 可否重复连接 
                graph.setMultigraph(false); 
                // 禁止改变元素大小 
                graph.setCellsResizable(false); 
                // 允许连线的目标和源是同一元素 
                graph.setAllowLoops(true); 

                // 开始往module里添加cell
                graph.getModel().beginUpdate();
                try
                {
                    //new一个cell   以单元的形式创建一个节点
                    var cell = new mxCell(null, new mxGeometry(100, 200, 100, 100), "一些样式配置");     
                    cell.vertex = true;
                    //插入这个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);
                    //插入线条设置连接图形
                    var e1 = graph.insertEdge(parent, null, '', v1, v2);

                    //预览时鼠标悬浮到节点时,改变鼠标样式
                    graph.getCursorForCell = function (cell) {
                      if (cell != null && cell.value != null && cell.vertex == 1) {
                         return 'pointer';
                      }
                    };
                }
                finally
                {
                    // 更新事务结束
                    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 必须对应一次 endUpdate
2.插入cell有两种方式:

new mxCell(null, new mxGeometry(100, 200, 100, 100))
graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30)
insertVertex 做了三件事,先是设置几何信息,然后创建一个节点,最后将这个节点添加到画布。insertEdge 与 insertVertex 类似,中间过程会调用 vertex.setEdge(true)Cell 标记为边。
几何信息四个数字分别对应 X、 Y、 宽、 高 坐标是以graph的左上角为原点。

3.最上面是针对图区域的一些设置
4.对插入元素的样式配置跟在mxCell()最后面的参数里,样式可以有很多,库里也提供了一些。就不多说,说一下自定义样式。
          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);
          //插入节点时定义样式
          var cell1 = new mxCell(null, new mxGeometry(100, 200, 100, 100), "img1");
5.添加按钮

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

分装一个动态添加不同功能的按钮的方法

        // 创建按钮
  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);
           };
         }
       },
     ]

//循环添加所有设置好功能的按钮
(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.建立拖拽关系(选取拖动定点添加)

// 检查图形中是否包含对应的elt节点
        function containsElt(graph, elt) {
          while (elt != null) {
            if (elt == graph.container) {
              return true;
            }

            elt = elt.parentNode;
          }

          return false;
        };

        // 返回鼠标选中的元素
        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;
        };

        // 在给定的位置插入一个元素 
        //mxCell是顶点和边的单元对象,mxCell从模型(mxGraph)那里复制了许多的方法
        //它们的主要差别在于,使用模型的方法会创建相关的事件通知以及撤销方法,使用单元的方法可以发生改变但不记录它们
        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);
          }
        };

        //创建一个DOM节点,作为拖动源
        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功能(这是为了跨浏览器平台而设计的,见下文) 
        if (mxClient.IS_IE) {
          mxEvent.addListener(img, 'dragstart', function (evt) {
            evt.returnValue = false;
          });
        }

        // 创建拖动源的预览
        var dragElt = document.createElement('div');
        dragElt.style.border = 'dashed black 1px';
        dragElt.style.width = '120px';
        dragElt.style.height = '40px';

        // 在点击拖动源图标时提供预览。 预览是提供的仅仅是拖动源的图片
        // 只有拖动源到容器内时才会显示元素的坐标预览

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

        //从拖动源拖动时显示导航线。
        //注意,对图形中已存在的元素拖动时显示导航线不在本方法约束范围。
        ds.isGuidesEnabled = function () {
          return graph.graphHandler.guidesEnabled;
        };

        //从拖动源拖动元素到图形以外的区域时,显示拖动源图片预览
        ds.createDragElement = mxDragSource.prototype.createDragElement;

var ds = mxUtils.makeDraggable(img, graphF, funct, dragElt, null, null, graph.autoscroll, true);
makeDraggable的参数makeDraggable( element, graphF, funct, dragElement, dx, dy, autoscroll, scalePreview, highlightDropTargets, getDropTarget)
详细信息请点这里


前人栽树,后人乘凉

关于mxGraph,官方API文档不友好,介绍简单不说还全是英文的,官网的Demo倒是不少,不过有的拉下来在自己的环境下跑会报错,同时国内对于mxGraph技术的文章真的是少之又少,学习过程简直就是摸着石头过河,目前只基本掌握基础使用部分,文章还会继续更新。希望可以帮助到有需要的朋友。

如果对你有帮助请帮我点个 喜欢~~~撒花🎉
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,947评论 4 60
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,067评论 1 32
  • 今天是2017年7月1日,一个特殊而值得纪念的日子。今天是我的生日,今天我就24岁了,24年前的今天,我的母亲忍...
    日光倾城_625c阅读 566评论 2 0
  • 又一年匆匆忙忙地结束了,匆忙得来不及数一下我们的脚步,匆忙得来不及听一下我们的足音。我们开始检点自己的日子,发现岁...
    mice411阅读 714评论 3 4