GoJS 使用笔记
作为商业软件,GoJs很容易使用,文档也很完备,不过项目中没有时间系统地按照文档学习,总是希望快速入门使用,所以将项目中遇到的问题精简一下,希望对后来者有些帮助。
开始使用
这里先展示一个最简单的例子,说明GoJs的使用。
<!DOCTYPE html> <!-- HTML5 document type -->
<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("");
}
GoJS 使用笔记的更多相关文章
- GoJS学习笔记
GoJS 和 GO 语言没有关系,它是一个用来创建交互式图表的 JavaScript 库. 基础概念 GraphObject 是所有图形是抽象基类,基本上 GoJS 中,万物皆 GraphObject ...
- GoJS学习笔记 (转)
目录 基础概念 开始绘制图形 1. 通过代码构建图形 2. 通过 GraphObject.make 构建图形 3. 使用 Model 和 Templates 创建图形 获取图形数据 获取所有 Node ...
- GoJS 教程新手入门(资源整理,解决方案)
以下几个是我在百度.谷歌 上能找到的比较全的GoJs的一些东西,希望对各位有所帮助! 如有外网网站不能访问请自行FQ GoJS官网 第一个推荐的是GoJS的一个类似于社区的问题讨论区,这里面初学者的一 ...
- 【Javascript】js图形编辑器库介绍
10个JavaScript库绘制自己的图表 jopen 2015-04-06 18:18:38 • 发布 摘要:10个JavaScript库绘制自己的图表 JointJS JointJS is a J ...
- git-简单流程(学习笔记)
这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
随机推荐
- Git 的基本命令的使用
1.获得Git仓库(克隆一份代码到本地仓库) git clone url 2.更新本地的代码 git pull 3.查看本地修改的文件 git status 4.将本地的修改加到stage中 git ...
- TensorRT 开始
TensorRT 是 NVIDIA 自家的高性能推理库,其 Getting Started 列出了各资料入口,如下: 本文基于当前的 TensorRT 8.2 版本,将一步步介绍从安装,直到加速推理自 ...
- C# 获取DPI例子
public static float GetDpiX() { System.Windows.Forms.Panel p = new System.Windows.Forms.Panel(); Sys ...
- CentOS 7安装Odoo 15社区版的详细操作指南
我之前的文章介绍过在Windows环境下安装Odoo 15,如果您需要在Windows部署,具体可参考我文末的微信号<10分钟教你本机电脑安装Odoo 15,并启用一个内置的项目APP应用> ...
- Android开发 定时任务清理数据
原文地址:Android开发 定时任务清理数据 | Stars-One的杂货小窝 公司项目,需要整定时任务,对数据进行清理,需要在每天凌晨0:00进行数据的清理,使用了Alarm和广播的方式来实现 P ...
- 全网最全的Java SpringBoot点赞功能实现
前言 最近公司在做一个NFT商城的项目,大致就是一个只买卖数字产品的平台,项目中有个需求是用户可以给商品点赞,还需要获取商品的点赞总数,类似下图 起初感觉这功能很好实现,无非就是加个点赞表嘛,后来发现 ...
- golang中的标准库http
Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现. http客户端 基本的HTTP/HTTPS请求 Get.Head.Post和PostForm函数发出HTTP/HTTP ...
- Maven生命周期,插件,单元测试junit
maven生命周期,maven命令,maven插件 maven生命周期:就是maven构建项目的过程,清理,编译,测试,报告,打包,安装,部署 maven命令:maven独立使用,通过命令,完成mav ...
- shell $用法
$0 这个程式的执行名字$n 这个程式的第n个参数值,n=1..9$* 这个程式的所有参数,此选项参数可超过9个.$# 这个程式的参数个数$$ 这个程式的PID(脚本运行的当前进程ID号)$! 执行上 ...
- 1.kafka
什么是Kafka 1.Apache Kafka是一个开源消息系统,由Scala写成. 2.Kafka是一个分布式消息队列.Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer ...