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 ...
随机推荐
- Nginx 基础入门
目录 Nginx 基础入门 1.Nginx简介 1.1.相关名词解释 2.Nginx优势 3.Nginx部署 4.Nginx配置文件 5.Nginx模块 6.Nginx配置文件 6.1.Locatio ...
- elementui-日期选择器时间清空报错踩坑
今天在项目中遇到了这个大坑 具体问题:在日期清空时会报错 解决方法:给日期绑定的值添加监听
- 1011day-人口普查系统
1.Browse.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" ...
- 【C++】STL算法
STL算法 标签:c++ 目录 STL算法 一.不变序列算法 1.熟悉的min(), max() 2.找最值还自己动手么?不了不了 3.熟悉的find()和新学会的count() 二.变值算法 1.f ...
- Web开发底层是Servlet
SpringMVC:是基于spring的一个框架,实际上就是spring的一个模块,专门是做web开发. 可以理解成servlet是一个升级 web开发底层是servlet,框架是在servlet基础 ...
- 【程序18】求s=a+aa+aaa+aaaa+aa...a的值
求s=a+aa+aaa+aaaa+aa-a的值,其中a是一个数字.例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制. 知识点:在Python 3里,reduce( ...
- 安卓开发常见Bug-项目未升级到Androidx
当项目未升级到androidx时,会出现某些项目文件资源不匹配的问题,建议在建立项目后就将项目升级到androidx 点击升级到androidx Migrate迁移然后点击左下角Dorefactor
- service层 必须做业务逻辑的处理
package com.aaa.zxf.service; import com.aaa.zxf.mapper.BookMapper; import com.aaa.zxf.model.Book; im ...
- 记一次.net core 异步线程设置超时时间
前言: 刷帖子看到一篇 Go 记录一次groutine通信与context控制 看了一下需求背景,挺有意思的,琢磨了下.net core下的实现 需求背景: 项目中需要定期执行任务A来做一些辅助的工作 ...
- 云服务器的windows系统上部署项目
1.购买云服务器 以百度云服务器为例 https://cloud.baidu.com/campaign/midyear2020/index.html?track=cp:npinzhuan|pf:pc| ...