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,一经查实,立即删除!

相关文章

do while的使用

while循环&#xff1a;while(条件){循环体;} do while循环&#xff1a;do{循环体;}while(条件); //注意do while 有分号 while循环和do while循环只有一个差别&#xff0c;就是&#xff1a;while循环先判断条件&#xff0c;成立才做循环体&#xff1b;do while循环则是先做循环…

Android学习笔记:TabHost 和 FragmentTabHost

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

转HTML、CSS、font-family:中文字体的英文名称

宋体 SimSun 黑体 SimHei 微软雅黑 Microsoft YaHei 微软正黑体 Microsoft JhengHei 新宋体 NSimSun 新细明体 PMingLiU 细明体 MingLiU 标楷体 DFKai-SB 仿宋 FangSong 楷体 KaiTi 仿宋_GB2312 FangSong_GB2312 楷体_GB2312 KaiTi_GB2312 宋体&#xff1a;SimSuncss中中文字体…

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;也…

基本概念-数据类型

参考&#xff1a;http://edu.51cto.com/roadmap/view/id-59.html5.数据类型5.1 数据类型可以使变量知道如何分配内存空间。例如&#xff0c;char类型占用1个字符&#xff0c;int通常占用4个字节5.2 C 语言常用的数据类型有 int sort 浮点型 double float字符串 char指针&#x…

android webview控件的缩放问题 隐藏缩放控件

利用java的反射机制 public void setZoomControlGone(View view) { Class classType; Field field; try { classType WebView.class; field classType.getDeclaredField("mZoomButtonsController"); field.setAccessible(true); ZoomButtonsController mZoomButton…

分布式概念与协议

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

C语言中的转义字符

在字符集中&#xff0c;有一类字符具有这样的特性&#xff1a;当从键盘上输入这个字符时&#xff0c;显示器上就可以显示这个字符&#xff0c;即输入什么就显示什么。这类字符称为可显示字符&#xff0c;如a、b、c、$、和空格符等都是可显示字符。 另一类字符却没有这种特性。它…

最常被程序员们谎称读过的计算机书籍

英文原文&#xff1a;Books Programmers Claim to Have Read 马克吐温曾经说过&#xff0c;所谓经典小说&#xff0c;就是指很多人希望读过&#xff0c;但很少人真正花时间去读的小说。这种说法同样适用于“经典”的计算机书籍。 在 Stack Overflow (以及其它很多软件论坛)上&…

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…

PCB布线规则

PCB布线有单面布线、双面布线及多层布线。布线的方式也有两种&#xff1a;自动布线及交互式布线&#xff0c;在自动布线之前&#xff0c;可以用交互式预先对要求比较严格的线进行布线&#xff0c;输入端与输出端的边线应避免相邻平行&#xff0c;以免产生反射干扰。必要时应加地…

strtok和strtok_r

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

ConTeXt 标题前后的空白

由于标题字一般都挺大&#xff0c;所以默认时标题之间的空白比较大&#xff0c;尤其是当多个标题在一起的时&#xff0c;空白就显得格外地大&#xff01; 要去除空白可以这样做&#xff1a;\setuphead[chapter][before\nowhitespace,after\nowhitespace] 当然&#xff0c;我们也…

PHP 实现简单的 倒计时 时分秒

// 以 YII框架为例&#xff1a; C 层代码public function actionIndex(){//php的时间是以秒算。js的时间以毫秒算date_default_timezone_set("Asia/Hong_Kong");//地区//配置每天的活动时间段$starttimestr "18:53:00";//转换为时间戳$starttimestr …

芯片封装名称说明

1、BGA(ball grid array)   球形触点陈列&#xff0c;表面贴装型封装之一。在印刷基板的背面按陈列方式制作出球形凸点用以代替引脚&#xff0c;在印刷基板的正面装配LSI 芯片&#xff0c;然后用模压树脂或灌封方法进行密封。也称为凸点陈列载体(PAC)。引脚可超过200&#xf…

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…

html5 的百度地图连接

在一些网站上&#xff0c;我们经常会看到一些地址会有一个图标的形式展现&#xff0c;当你点击的时候就会加载一个你点击区域的地图出来&#xff0c;很神奇的一个功能&#xff0c;在之前是没有这样功能的&#xff0c;都是直接写上地址&#xff0c;你要去的话自己找去吧&#xf…

分享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的文件。 支持的文件格…