mxgraph.js绘图工具二次开发总结

MxGraph

一、更换 js 和 css 引用路径

1. link 和 script 里的路径就不用说了
2. 更改全局变量

	mxBasePath = "/static/js/conceptualModel/js/mxgraph/";
	STYLE_PATH = "/static/js/conceptualModel/styles";
	RESOURCES_PATH = "/static/js/conceptualModel/resources";
	IMAGE_PATH = "/static/js/conceptualModel/images";
	STENCIL_PATH = "/static/js/conceptualModel/stencils";

二、GraphEditor 文件执行顺序

可以通过改变全局变量,控制各个组件的高宽。

	EditorUi.prototype.menubarHeight = 0;
	
	EditorUi.prototype.formatWidth = 300;
	
	EditorUi.prototype.toolbarHeight = 0;

三、自定义左侧侧边栏

1. 添加tab栏

Sidebar.prototype.addTabsPalette = function (expand) {
    var elt = document.createElement("ul");
    elt.className = "mtTabs";
    this.container.appendChild(elt);
    var li1 = document.createElement("li");
    var a1 = document.createElement("a");
    li1.className = "active";
    li1.addEventListener("click", function (e) {
        var tabContents = document.getElementsByClassName("tabContent")[0].childNodes;
        var className = this.className;
        if (className.indexOf("active") < 0) {
            var children = this.parentNode.childNodes;
            for (var i = 0; i < children.length; i++) {
                children[i].className = "";
            }
            this.className = "active";
            tabContents[0].className = "tab-pane fade active";
            tabContents[1].className = "tab-pane fade";
        }
    });
    a1.innerText = "GeoIcons";
    li1.appendChild(a1);
    var li2 = document.createElement("li");
    var a2 = document.createElement("a");
    a2.innerText = "General";
    li2.appendChild(a2);
    li2.addEventListener("click", function (e) {
        var tabContents = document.getElementsByClassName("tabContent")[0].childNodes;
        var className = this.className;
        if (className.indexOf("active") < 0) {
            var children = this.parentNode.childNodes;
            for (var i = 0; i < children.length; i++) {
                children[i].className = "";
            }
            this.className = "active";
            tabContents[1].className = "tab-pane fade active";
            tabContents[0].className = "tab-pane fade";
        }
    });

    elt.appendChild(li1);
    elt.appendChild(li2);

    this.palettes['tabs'] = elt;
};
Sidebar.prototype.addTabContent = function (expand) {
    var elt = document.createElement("div");
    elt.className = "tabContent";
    this.container.appendChild(elt);

    var panel1 = document.createElement("div");
    panel1.className = "tab-pane fade active";
    elt.appendChild(panel1);

    var panel2 = document.createElement("div");
    panel2.className = "tab-pane fade";
    elt.appendChild(panel2);

    this.palettes['tabContent'] = elt;
};

2. 添加自定义内容

Sidebar.prototype.addGeoIconPalette = function (expand) {
    var that = this;
    $.ajax({
        url: "/conceptualModel/getAllGeoIcons",
        type: "get",
        success: function (result) {
            var iconList = result;
            var tabContents = that.palettes['tabContent'].childNodes;
            var elt = document.createElement('div');
            tabContents[0].appendChild(elt);

            var div = document.createElement('div');
            div.className = 'geSidebar';
            div.id = 'geoIconContainer';
            div.style.boxSizing = 'border-box';
            div.style.overflow = 'hidden';
            div.style.width = '100%';
            div.style.padding = '8px';
            div.style.paddingTop = '14px';
            div.style.paddingBottom = '0px';
            elt.appendChild(div);
            for (var i = 0; i < iconList.length; i++) {
                var name = iconList[i].name;
                var iconId = iconList[i].geoId;
                var a = that.createGeoIconTemplate('image;html=1;labelBackgroundColor=#ffffff;image=' + iconList[i].pathUrl,
                    that.defaultImageWidth, that.defaultImageHeight, "", name, name != null, null, null, iconId);
                div.appendChild(a);
            }
        }
    });
};
//自定义创建方法,跳过 addEntry() 这一步,提高加载效率(×),搜索效率
Sidebar.prototype.createGeoIconTemplate = function (style, width, height, value, title, showLabel, showTitle, allowCellsInserted, iconId) {
    var cells = [new mxCell((value != null) ? value : '', new mxGeometry(0, 0, width, height), style)];
    cells[0].vertex = true;
    cells[0].geoId = iconId;

    return this.createVertexTemplateFromCells(cells, width, height, title, showLabel, showTitle, allowCellsInserted);
};
//地理图标tab的搜索功能
Sidebar.prototype.searchEntries = function (searchTerms, count, page, success, error) {
    // 拿着 searchTerms 去数据库里搜索
    if (this.activeTerms == '' || this.activeTerms != searchTerms){
        this.activeTerms = searchTerms;
        $.ajax({
            url:'/conceptualModel/getGeoIconsByKey',
            data:{
                searchTerms:searchTerms
            },
            success:(result)=> {
                // 填充字典
                for (var i=0; i< result.length; i++){
                    var name = result[i].name;
                    var iconId = result[i].geoId;
                    var tags = [this.activeTerms, name];
                    this.createVertexTemplateEntry('image;html=1;labelBackgroundColor=#ffffff;image=' + result[i].pathUrl,
                        this.defaultImageWidth, this.defaultImageHeight, "", name, name != null, null, tags.join(" "), iconId);
                }
                // 该方法的源码
                ...
            }
        });

    } else if (this.activeTerms == searchTerms){
        //该方法的源码
        ...
    }
};

3. 添加折叠框

//GraphEditor自带函数
Sidebar.prototype.addPaletteFunctions = function (id, title, expanded, fns) {
    this.addPalette(id, title, expanded, mxUtils.bind(this, function (content) {
        for (var i = 0; i < fns.length; i++) {
            content.appendChild(fns[i](content));
        }
    }));
};
Sidebar.prototype.addPalette = function (id, title, expanded, onInit) {
	...
}

四、自定义右侧侧边栏

1. 入口在 refresh() 函数中

Format.prototype.refresh = function () {
	...
	if(graph.isSelectionEmpty()){
		//未选中任何元素
	}
	else if(graph.isEditing()){
		//正在编辑元素
	}
	else{
		//选中要素时,再接着区分不同情景
		if(graph.getSelectionCount() > 1){}
		else if(cell.style == "group"){}
		else if(cell.isEdge()){}
		else if(ss.image || !cell.isEdge()){}
		else{}
	}
	...
}

2. 添加自定义内容

this.panels.push(new GeoElementsPanel(this,ui,div));
...
GeoElementsPanel = function (format, editorUi, container) {
    BaseFormatPanel.call(this, format, editorUi, container);
    this.init();
};

mxUtils.extend(GeoElementsPanel, BaseFormatPanel);

GeoElementsPanel.prototype.init = function () {
    this.container.appendChild(this.addGeneralElements(this.createPanel()));
    this.container.appendChild(this.addGeoElementSelect(this.createPanel()));
};

五、自定义右击弹出菜单

1. GraphEditor的方式

2. 更简单直接的方式

//可以动态添加二级菜单,以及二级菜单对应的方法
var submenu1 = menu.addItem('关联到...', null, null);
menu.addItem('几何形状', null, function(){
	relate2Shape();
}, submenu1);

var subProperty = menu.addItem('属性特征', null, null, submenu1);
for (let i = 0; i < GeoElements.properties.length; i++) {
	menu.addItem(GeoElements.properties[i].type, null, function(){
		relate2Property(GeoElements.properties[i].type);
	}, subProperty);
}

在这里插入图片描述

六、导出局部图形 xml → png

前端

var relate2Shape =  ()=> {
        var ss = graph.getSelectionCell();

        var xmlDoc = mxUtils.createXmlDocument();
        var root = xmlDoc.createElement('output');
        xmlDoc.appendChild(root);
        var xmlCanvas = new mxXmlCanvas2D(root);
        
        var scale=graph.getView().scale;
        var stackLayout = new mxStackLayout(graph, true);
        var border=stackLayout.border;
        var bounds = graph.getGraphBounds();
        
        //求选中图形的坐标,坐标原点在左上
        var cells = graph.model.cells;
        var x = ss.geometry.x;
        var y = ss.geometry.y;
        for(let i in cells){	 // cells 类数组对象 没有长度属性 和 数组的方法
            if (i > 1 && cells[i].parent.id == 1 && cells[i].edge != true) {
                if (cells[i].geometry.x < x  ) {
                    x = cells[i].geometry.x;
                }
                if (cells[i].geometry.y < y ) {
                    y = cells[i].geometry.y;
                }
            }
        }
        //整幅图与局部图的坐标差
        var dX = x - ss.geometry.x;
        var dY = y - ss.geometry.y;
        xmlCanvas.translate(
            Math.floor((border / scale - bounds.x + dX) / scale),
            Math.floor((border / scale - bounds.y + dY) / scale),
        );
        xmlCanvas.scale(1);
        var graphRoot = _.cloneDeep(graph.model.root); //lodash的方法
        //剔除没有选中的要素
        for (var i = 0;i<graphRoot.children[0].children.length;i++){
            var cell = graphRoot.children[0].children[i];
            if (cell != ss){
                graphRoot.children[0].children.splice(i,1);
            }
        }
        var imgExport = new mxImageExport();
        imgExport.drawState(graph.getView().getState(graphRoot), xmlCanvas);
        var w = ss.geometry.width;
        var h = ss.geometry.height;
        var xml = mxUtils.getXml(root);

        $.ajax({
            url: "/userImage/userDIY",
            data: {
                width: w,
                height: h,
                xml: xml,
                type: "几何形状",
				conceptName:GeoElements.name
            },
            type: "post",
            async: true,
            success: (userImage)=>{
                graph.clearSelection();
                $("#geoElementSelect").val("几何形状");
                //js原生触发DOM事件
                $("#geoElementSelect").trigger("change");
				...
                GeoElements.shapeInfo.relateImages.push(userImage);
            }
        })
    };

后端:


	@RequestMapping(value = "/userDIY",method = RequestMethod.POST)
    UserImage userDIY(@RequestParam("width") String width,@RequestParam("height") String height,
                      @RequestParam("xml") String xml,@RequestParam("type") String type,
                      @RequestParam("conceptName")String conceptName) throws Exception {
	    ...
	    //将图片保存到本地
	    MxGraphUtils mxGraphUtils = new MxGraphUtils();
    	mxGraphUtils.exportImage((int)Double.parseDouble(width),(int)Double.parseDouble(height), xml, resourcePath+"/userImage/", name);
	    ...
        return userImage;
    }
	

	public void exportImage(int w, int h,String xml,String path,String name) throws Exception {
        try {
            logger.info(""+w+" "+h);
            logger.info(xml);
            logger.info(path);
            logger.info(name);
            long t0 = System.currentTimeMillis();
            BufferedImage image = mxUtils.createBufferedImage(w, h, Color.WHITE);
            logger.info("1");
            // Creates handle and configures anti-aliasing
            Graphics2D g2 = image.createGraphics();
            mxUtils.setAntiAlias(g2, true, true);
            long t1 = System.currentTimeMillis();
            logger.info("2");
            // Parses request into graphics canvas
            mxGraphicsCanvas2D gc2 = new mxGraphicsCanvas2D(g2);
            parseXmlSax(xml, gc2);
            long t2 = System.currentTimeMillis();

            logger.info("before mkdirs");

            File file = new File(path);
            if (!file.exists() && !file.isDirectory()) {
                file.mkdirs();
            }

            logger.info("after mkdirs");

            ImageIO.write(image, "png", new File(path + name));
            long t3 = System.currentTimeMillis();

            logger.info("saved");
        }
        catch (Exception e){
            logger.error(e.getMessage());
            logger.error(e.toString());
        }

    }

最后为了方便大家的沟通与交流请加QQ群: 625787746

请进QQ群交流:【IT博客技术分享群①】:https://jq.qq.com/?_wv=1027&k=DceI0140

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT博客技术分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值