GoJS 使用笔记

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
作为商业软件,GoJs很容易使用,文档也很完备,不过项目中没有时间系统地按照文档学习,总是希望快速入门使用,所以将项目中遇到的问题精简一下,希望对后来者有些帮助。

开始使用

这里先展示一个最简单的例子,说明GoJs的使用。

html> 
<html><head><script src="https://unpkg.com/gojs/release/go-debug.js">script>
head><body><div id="myDiagramDiv" style="width:1200px; height:850px; background-color: #DAE4E4;">div><script>var $ = go.GraphObject.make;myDiagram =$(go.Diagram, "myDiagramDiv",{"undoManager.isEnabled": true});myDiagram.add($(go.Node, "Auto",$(go.Shape, "RoundedRectangle",{fill: $(go.Brush, "Linear",{ 0.0: "Violet", 1.0: "Lavender" })}),$(go.TextBlock, "测试文本!",{ margin: 5 }),$("Button", {alignment: go.Spot.Right,alignmentFocus: go.Spot.Left,click: function(e,obj){ console.log(e); console.log(obj);alert(obj);}},$(go.TextBlock, "+", // the Button content{ font: "bold 8pt sans-serif" }))));script>
body>html>

首先是引用GoJs库,可以有多个途径下载,可以通过npm,nuget等包管理工具,也可以直接下载。我们这里使用unpkg的引用。
然后就是使用 go.GraphObject.make创建图形和图形中的元素。这里先将 go.GraphObject.make简化定义为,方便代码的编写与阅读,注意这不是必须的,也可以使用,方便代码的编写与阅读,注意这不是必须的,也可以使用便使$或者其它简化方式。结果如下:

这里的关键是go.GraphObject.make的使用,下面重点介绍这个函数。

使用go.GraphObject.make创建对象

一个图形可以看做由节点和连线组成,在GoJs中,图形元素是GraphObject,我们可以使用代码创建节点:

  var node = new go.Node(go.Panel.Auto);var shape = new go.Shape();shape.figure = "RoundedRectangle";shape.fill = "lightblue";node.add(shape);var textblock = new go.TextBlock();textblock.text = "你好!";textblock.margin = 5;node.add(textblock);diagram.add(node);

这种办法属于常规的编程方法,容易理解,但是需要定义大量的中间变量,如果需要创建的元素很多,就会感觉有些冗余。因此GoJs使用创建函数go.GraphObject.make简化创建过程。上面的代码写为:

  var $ = go.GraphObject.make;diagram.add($(go.Node, "Auto",$(go.Shape, "RoundedRectangle", { fill: "lightblue" }),$(go.TextBlock, "你好!", { margin: 5 })));

可读性好多了。 go.GraphObject.make的第一个参数是需要创建的类型,通常是GraphObject的子类,后续的参数可以有多个,可以是以下类型:

  • 属性/值对的JS简单对象,说明被创建对象的属性。
  • GraphObject,添加到被创建对象中的子对象,比如,上面的代码中在Node中增加Shape和TextBlock。
  • 字符串,针对特定对象的属性,比如对于TextBlock,就是设置文本值
  • 其它可能的Js对象,针对创建对象的不同。

Binding

基本用法

Binding顾名思义是绑定,将模型的属性与GraphObject对象的属性进行绑定。比如,有如下模型:

{ key: 23, say: "你好!" }

我们需要将say属性绑定到文本对象的text属性,可以使用下面的代码:

 var $ = go.GraphObject.make;myDiagram.nodeTemplate =$(go.Node, "Auto",. . .$(go.TextBlock, new go.Binding("text", "say")))

转换函数

如果我们希望绑定的属性进行转换,可以使用转换函数,比如:

  new go.Binding("text", "say", function(v) { return "我说: " + v; })

单向绑定和双向绑定

单向绑定时只能是模型的属性改变GraphObject对象的属性,而双向绑定时,GraphObject对象的属性的改变可以改变模型的属性。双向绑定的写法是这样的:

new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)

当loc转换为location时,使用go.Point.parse函数,当location转换为loc时,使用go.Point.stringify函数。

GraphObject

GraphObject是抽象类,不能直接创建,GraphObject具有下面的一些特性。

尺寸

GraphObject有关尺寸的属性如下:

  • desiredSize,minSize和maxSize。width和height会转换为desiredSize。
  • angle和scale
  • stretch

GraphObject在Panel中绘制完成后,会有如下的只读属性:

  • nuturalBounds:表示基本尺寸,不受转换的影响
  • measureBounds: 表示在包含它的Panel中的尺寸
  • actualBounds:表示在包含它的Panel中的实际尺寸

顶层GraphObject一定是Part

Part是GraphObject的子类,表示顶层元素。顶层元素一定是Part,包括Node,Linke,Group以及Adornment,下面的属性用于获取相关的GraphObject:

  • panel:获取包含这个GraphObject的Panel
  • part: 获取这个GraphObject所在的Part。
  • layer: 获取这个GraphObject所在的Layer。
  • diagram:获取所在的Diagram。

Model

模型中保存了图形显示的数据,描述基本实体、它们的属性以及之间的关系。模型中的数据要尽量简单,可以很容易地序列化为JSON或者XML格式。有两种模型TreeModel和GraphLinksModel,前者保存树状结构的数据。模型中key值用来标识对象,必须是唯一的,下面是Model的使用实例:

myDiagram.model = new go.TreeModel();myDiagram.model.nodeDataArray = [{ "key": 0, "text": "Mind Map", "loc": "0 0" },{ "key": 1, "parent": 0, "text": "Getting more time", "brush": "skyblue", "dir": "right", "loc": "77 -22" },{ "key": 11, "parent": 1, "text": "Wake up early", "brush": "skyblue", "dir": "right", "loc": "200 -48" },{ "key": 12, "parent": 1, "text": "Delegate", "brush": "skyblue", "dir": "right", "loc": "200 -22" }];

这是树形结构的数据。如果保存一般的图结构,需要使用GraphLinksModel。

自定义Node模板

个人认为方便的自定义模板是GoJs的强大功能之一,使用nodeTemplateMap可以很方便地定义各种类型的模板,只要在数据模型中指定模板的名称(使用category),就可以显示相应的图形。nodeTemplateMap的使用方法如下:

myDiagram.nodeTemplateMap.add("End",part);

这里,part就是显示的模板,比如,下面是一个part的定义,显示状态图的开始节点:

var partStart=    $(go.Node, "Spot", { desiredSize: new go.Size(75, 75) },new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),$(go.Shape, "Circle",{fill: "#52ce60", /* green */stroke: null,portId: "",fromLinkable: true, fromLinkableSelfNode: true, fromLinkableDuplicates: true,toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true,cursor: "pointer"}),$(go.TextBlock, "开始",{font: "bold 16pt helvetica, bold arial, sans-serif",stroke: "whitesmoke"}));

对应的数据如下:

 {"id":-1, "loc":"155 -138", "category":"Start"}

数据中,category指定了模板类型,loc绑定到图元的位置,这里是双向绑定,也就是图元位置的变化,会改变数据模型中的数据。

如果只定义通用的模板,可以使用:

myDiagram.nodeTemplate=part;

这种情况下,没有指定category的数据都采用缺省模板显示。

自定义选中模板

当一个节点被选中时,我们希望使用不同的模板显示,比如,状态图中,一个被选中的节点中会出现添加链接的按钮,选中前:

选中后:

如果为使用缺省模板的节点定义选中模板,可以直接定义:

 myDiagram.nodeTemplate.selectionAdornmentTemplate = adornmentTemplate;

如果需要为使用nodeTemplateMap添加的自定义模板定义选中模板,可以使用如下方法:

var partStart=    $(go.Node, "Spot", { desiredSize: new go.Size(75, 75) },new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),$(go.Shape, "Circle",{fill: "#52ce60", /* green */stroke: null,portId: "",fromLinkable: true, fromLinkableSelfNode: true, fromLinkableDuplicates: true,toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true,cursor: "pointer"}),$(go.TextBlock, "开始",{font: "bold 16pt helvetica, bold arial, sans-serif",stroke: "whitesmoke"}));partStart.selectionAdornmentTemplate =$(go.Adornment, "Spot",$(go.Panel, "Auto",$(go.Shape, "RoundedRectangle", roundedRectangleParams,{ fill: null, stroke: "#7986cb", strokeWidth: 3 }),$(go.Placeholder)  // a Placeholder sizes itself to the selected Node),// the button to create a "next" node, at the top-right corner$("Button",{alignment: go.Spot.TopRight,click: addNodeAndLink  // this function is defined below},$(go.Shape, "PlusLine", { width: 6, height: 6 })));myDiagram.nodeTemplateMap.add("Start",partStart);

上面的代码中,需要先定义模板的part,然后增加选中模板,最后,使用nodeTemplateMap.add方法进行添加。

节点和连接的上下文菜单

对于节点和菜单的缺省模板,可以直接使用contextMenu定义上下文菜单,比如:

        myDiagram.nodeTemplate.contextMenu =$("ContextMenu",$("ContextMenuButton",$(go.TextBlock, "显示属性"),{ click: function (e, obj) { var data = myDiagram.model.findNodeDataForKey(obj.part.key);alert(data.complex.p1); } })); 

对于使用nodeTemplateMap定义的自定义模板,需要在模板上先定义上下文菜单,然后再将模板添加到nodeTemplateMap中,下面的代码定义了状态图中结束节点的上下文菜单:

var partEnd=$(go.Node, "Spot", { desiredSize: new go.Size(75, 75) },new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),$(go.Shape, "Circle",{fill: "maroon",stroke: null,portId: "",fromLinkable: true, fromLinkableSelfNode: true, fromLinkableDuplicates: true,toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true,cursor: "pointer"}),$(go.Shape, "Circle", { fill: null, desiredSize: new go.Size(65, 65), strokeWidth: 2, stroke: "whitesmoke" }),$(go.TextBlock, "结束",{font: "bold 16pt helvetica, bold arial, sans-serif",stroke: "whitesmoke"}));partEnd.contextMenu=$("ContextMenu",$("ContextMenuButton",$(go.TextBlock, "显示属性"),{ click: function (e, obj) { var data = myDiagram.model.findNodeDataForKey(obj.part.key);alert(data.complex.p1); } })); myDiagram.nodeTemplateMap.add("End",partEnd);

连接的上下文菜单定义与节点相同,示例代码如下:

        myDiagram.linkTemplate.contextMenu =$("ContextMenu",$("ContextMenuButton",$(go.TextBlock, "显示属性"),{ click: function (e, obj) { alert(obj.part.data.expression); } })); 

节点和连接关联数据的访问

很多图形编辑器不容易使用的一个原因是编辑器的数据模型与业务的数据模型很难匹配。业务数据模型经常比较复杂,不仅仅是键值对能够完全表示的,很多情况下需要使用复杂的对象描述。GoJs在这一点上做得非常好,图形相关的数据模型可以和图形进行绑定,并且数据模型中可以包括复杂的数据对象,比如下面的节点中包括了复合的对象:

{"id":-1, "loc":"155 -138", "category":"Start","complex":{"p1":"自定义属性"}}

对象的读取也不复杂,在访问节点数据的示例代码如下:

        myDiagram.nodeTemplate.contextMenu =$("ContextMenu",$("ContextMenuButton",$(go.TextBlock, "显示属性"),{ click: function (e, obj) { var data = myDiagram.model.findNodeDataForKey(obj.part.key);alert(data.complex.p1); } })); 

访问连接数据的示例代码如下:

myDiagram.linkTemplate.contextMenu =$("ContextMenu",$("ContextMenuButton",$(go.TextBlock, "显示属性"),{ click: function (e, obj) { console.log(e);console.log(obj.part);alert(obj.part.data.expression); } })); 

GoJs的命令

GoJs的命令,比如删除、重做、取消等等通过类CommandHandler实现。命令可以通过代码执行,也可以通过快捷键执行。下面的代码执行undo操作:

 myDiagram.commandHandler.undo();

下面是GoJs常用的命令和对应的快捷键:

  • Del 或者 Backspace 激活 CommandHandler.deleteSelection,删除
  • Ctrl-X 或者 Shift-Del 激活 CommandHandler.cutSelection,剪切
  • Ctrl-C 或者 Ctrl-Insert 激活 CommandHandler.copySelection,拷贝
  • Ctrl-V 或者 Shift-Insert 激活 CommandHandler.pasteSelection,粘贴
  • Ctrl-A 激活 CommandHandler.selectAll,全选
  • Ctrl-Z 或者 Alt-Backspace 激活 CommandHandler.undo,取消
  • Ctrl-Y 或者 Alt-Shift-Backspace 激活 CommandHandler.redo,重做
  • 空格键 激活 CommandHandler.scrollToPart,滚动到部件
    • (减号)激活CommandHandler.decreaseZoom,缩小zoom
    • (加号)激活 CommandHandler.increaseZoom,放大zoom
  • Ctrl-0 激活 CommandHandler.resetZoom ,重置zoom
  • Shift-Z 激活 CommandHandler.zoomToFit,设置zoom到适合图形大小
  • Ctrl-G 激活 CommandHandler.groupSelection , 组合
  • Ctrl-Shift-G 激活 CommandHandler.ungroupSelection,取消组合
  • F2 激活 CommandHandler.editTextBlock,编辑
  • Esc 激活 CommandHandler.stopCommand,取消命令

GoJs 上下文菜单

前面介绍了节点和链接的上下文菜单,在图形的背景上也可以设置上下文菜单,设置方法很简单,直接在背景的contextMenu上设置就可以了,示例代码如下:

    myDiagram.contextMenu =GO("ContextMenu",GO("ContextMenuButton",GO(go.TextBlock, "撤销"),{click: function (e, obj) {myDiagram.commandHandler.undo();}}));

可以对ContextMenuButton设置尺寸,比如,增加宽和高的属性:

GO("ContextMenuButton",GO(go.TextBlock, "撤销"),{width: 160, height: 120, click: function (e, obj) {myDiagram.commandHandler.undo();}}),

也可以为ContextMenu设置属性,添加完菜单按钮后面,增加属性设置:

myDiagram.contextMenu =GO("ContextMenu",GO("ContextMenuButton",GO(go.TextBlock, "撤销"),{click: function (e, obj) {myDiagram.commandHandler.undo();}}),GO("ContextMenuButton",GO(go.TextBlock, "重做"),{click: function (e, obj) {myDiagram.commandHandler.redo();}}),{width:200});

GoJs 生成图片并回传服务器

GoJs提供在客户端生成流程图的blob数据,然后通过浏览器进行下载,这种方式不需要服务端的支持,示例代码如下:

myDiagram.makeImageData({ returnType: "blob", scale: 3, detail: 0.9, callback: saveBlobToServer});

这里生成的blob数据会由自定义的回调函数处理,在回调函数中,可以编写通过浏览器的下载代码,或者将流程图数据回传到服务器的代码。这里,我们希望将图片回传服务器进行保存:

            function saveBlobToServer(blob) {var fd = new FormData();fd.append('fname', 'myBlobFile.png');fd.append('data', blob);$.ajax({type: 'POST',url: root + 'Upload/SaveImage',data: fd,processData: false,contentType: false}).done(function (data) {if (!data) alert("保存完成");else alert(data);});}

服务器端使用Asp.Net Core:

       [HttpPost]public IActionResult SaveImage(){var files = Request.Form.Files;var fn = Request.Form["fname"];if (files.Count > 0){var pic = files[0];var fileName = fn;// Path.Combine(rootpath, pic.FileName);if (System.IO.File.Exists(fileName)) System.IO.File.Delete(fileName);using (var stream = new FileStream(fileName, FileMode.CreateNew)){pic.CopyTo(stream);}}return Content("");}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android学习笔记:TabHost 和 FragmentTabHost

2019独角兽企业重金招聘Python工程师标准>>> Android学习笔记&#xff1a;TabHost 和 FragmentTabHostTabHost命名空间&#xff1a;android.widget.TabHost初始化函数&#xff08;必须在addTab之前调用&#xff09;&#xff1a;setup(); 包含两个子元素&#xff1a;…

PostgreSQL VACUUM 之深入浅出 (二)

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 AUTOVACUUM AUTOVACUUM 简介 PostgreSQL 提供了 AUTOVACUUM 的机制。 autovacuum 不仅会自动进行 VACUUM&#xff0c;也…

分布式概念与协议

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 分布式协议 分布式理论概念 1. 分布式数据一致性 分布式数据一致性&#xff0c;指的是数据在多个副本中存储时&#xff…

java Web监听器导图详解

监听器是JAVA Web开发中很重要的内容&#xff0c;其中涉及到的知识&#xff0c;可以参考下面导图&#xff1a; Web监听器 1 什么是web监听器&#xff1f; web监听器是一种Servlet中的特殊的类&#xff0c;它们能帮助开发者监听web中的特定事件&#xff0c;比如ServletContext,H…

Linux C/C++ UDP Socket 网络通信

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 昨晚 Vv 让我给她讲讲网络编程&#xff0c;于是我就傻乎乎的带她入了门… 以下内容为讲课时制作的笔记&#xff5e; 1. sock…

strtok和strtok_r

strtok和strtok_r原型&#xff1a;char *strtok(char *s, char *delim); 功能&#xff1a;分解字符串为一组字符串。s为要分解的字符串&#xff0c;delim为分隔符字符串。 说明&#xff1a;首次调用时&#xff0c;s指向要分解的字符串&#xff0c;之后再次调用要把s设成NULL。 …

Django ORM

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 目录* Django ORM ORM实操之数据库迁移 ORM实操之字段的修改 ORM实操之数据的增删改查 数据库同步 ORM创建表关系 Dja…

分享25个高质量的移动设备wordpress主题(Mobile theme)

日期&#xff1a;2012-9-10 来源&#xff1a;GBin1.com wordpress毋庸置疑是占有量最大的博客管理系统。提供强大的功能和使用的主题及其自定义模块。随着移动互联网的发展&#xff0c;更多的人开始使用移动设备访问互联网&#xff0c;为了更好的迎合用户的需要&#xff0c;我…

.NET NPOI导出Excel详解

http://www.cnblogs.com/yinrq/p/5590970.html .NET NPOI导出Excel详解 NPOI&#xff0c;顾名思义&#xff0c;就是POI的.NET版本。那POI又是什么呢&#xff1f;POI是一套用Java写成的库&#xff0c;能够帮助开发者在没有安装微软Office的情况下读写Office的文件。 支持的文件格…

c++隐式类型转换存在的陷阱

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 目录* 目标代码 构造函数定义的隐式类型转换分析a1分析a2分析a3 1|0目标代码 旨在弄懂下面的代码&#xff0c;明确变量a1…

Android中将一个图片切割成多个图片[转]

有种场景&#xff0c;我们想将一个图片切割成多个图片。比如我们在开发一个拼图的游戏&#xff0c;就首先要对图片进行切割。 以下是封装好的两个类&#xff0c;可以实现图片的切割。仅供参考和学习。 一个是ImagePiece类&#xff0c;此类保存了一个Bitmap对象和一个标识图片的…

并行开发 —— 第六篇 异步编程模型

在.net里面异步编程模型由来已久&#xff0c;相信大家也知道Begin/End异步模式和事件异步模式&#xff0c;在task出现以后&#xff0c;这些东西都可以被task包装 起来&#xff0c;可能有人会问&#xff0c;这样做有什么好处&#xff0c;下面一一道来。 一&#xff1a; Begin/En…

C++相关

初始化列表中的初始化顺序1 class Printer{2 public:3 Printer(string name){cout<<name;}4 };5 class Container{6 public:7 Container():b("b"),a("a"){}8 Printer a;9 Printer b; 10 }; 11 12 int main…

Java中的Unsafe在安全领域的一些应用总结和复现

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 目录* 0 前言 1 基本使用 1.1 内存级别修改值1.2 创建对象1.3 创建VM Anonymous Class 2 利用姿势 2.1 修改值以关闭RASP等…

Spring Boot中使用Swagger2构建强大的RESTful API文档

由于Spring Boot能够快速开发、便捷部署等特性&#xff0c;相信有很大一部分Spring Boot的用户会用来构建RESTful API。而我们构建RESTful API的目的通常都是由于多终端的原因&#xff0c;这些终端会共用很多底层业务逻辑&#xff0c;因此我们会抽象出这样一层来同时服务于多个…

关于公司没有公网IP也没有动态IP,如何远程办公呢?

2019独角兽企业重金招聘Python工程师标准>>> 迫于公司网络环境特殊&#xff0c;没有公网IP地址&#xff0c;也没有动态IP地址&#xff0c;其实就是园区分了一根内网固定IP的网线过来&#xff0c;这两天正巧有同事要外出办公&#xff0c;问题来了&#xff0c;开发环境…

ST_LINK/V2 SWIM和SWD、JTAG下载口说明

LED状态说明 闪烁红色&#xff1a;ST-LINK/V2连接到计算机后&#xff0c;第一次USB枚举过程红色&#xff1a;ST-LINK/V2与计算机已建立连接闪烁绿色/红色&#xff1a;目标板和计算机在进行数据交换绿色&#xff1a;通讯完成橙色&#xff08;红色绿色&#xff09;&#xff1a;通…

Gerrit的用法及与gitlab的区别

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 来到一个新的团队&#xff0c;开发的代码被同事覆盖了。找同事核实&#xff0c;同事却说根本没有看到我的代码。经过一番沟通…

The 15th UESTC Programming Contest Preliminary H - Hesty Str1ng cdoj1551

地址&#xff1a;http://acm.uestc.edu.cn/#/problem/show/1551 题目&#xff1a; Hesty Str1ng Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) A chrysanthemum was painted on the second page, and we tried to use the magic pow…

easyui 点滴记录

为什么80%的码农都做不了架构师&#xff1f;>>> 【1.安装】&#xff1a;pip install easygui 【2.常用】 integerbox 交互式输入数字textbox 交互式输入文本ccbox 确认判断boolbox 是否判断multchoicebox 多选条目choicebox 单选条目buttonbox 单选按钮【3.体验一…