前端交互式图表绘制库GoJS介绍
栏目: JavaScript · 发布时间: 5年前
内容简介:经人介绍看了下前端的图形绘制库GoJS,感觉挺强大的,特别适合来制作图形绘制工具。因为有需求要提供一个基于Web的图形编程工具,用它当前端看来是个不错的选择。唯一需要注意的是这东东不是免费的,而且还有些小贵,单应用单域名永久授权就要7千美刀,大伙们看情况使用吧。不过它提供带水印的试用版,可以用来先开发程序,网上也有各种去水印的方法,这里我就不提了。首先,获取GoJS库,你可以用下面npm命令将JS库和范例都下载到本地。发布版的JS文件在”release”目录下。或者,你也可以在你的HTML文件头里引用GoJ
经人介绍看了下前端的图形绘制库GoJS,感觉挺强大的,特别适合来制作图形绘制工具。因为有需求要提供一个基于Web的图形编程工具,用它当前端看来是个不错的选择。唯一需要注意的是这东东不是免费的,而且还有些小贵,单应用单域名永久授权就要7千美刀,大伙们看情况使用吧。不过它提供带水印的试用版,可以用来先开发程序,网上也有各种去水印的方法,这里我就不提了。
首先,获取GoJS库,你可以用下面npm命令将JS库和范例都下载到本地。
$ npm install gojs --save
发布版的JS文件在”release”目录下。或者,你也可以在你的HTML文件头里引用GoJS的CDN
<script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/1.8.36/go.js"></script>
拿到JS文件后,我们就来写个官方的QuickStart范例。先写HTML,为了简化,文件体里面就一个DIV元素,用来绑定图表。
<body> <!-- 图表DIV必须指定尺寸,同时建议给个背景颜色可以分辨出图表画布 DIV的id很重要,在后面的JS里就是靠这个id来绑定具体GoJS对象的 --> <div id="myDiagramDiv" style="width:800px; height:500px; background-color:#DAE4E4;"></div> </body>
接下来,我们就要写JS了。GoJS是一个MVVM模型,也就是Model模型同View视图的双向绑定,类似于AngularJS。View部分包括了整个图表Diagram风格的定义,节点Node的定义,和连线Link的定义。我们一步步来,从视图开始,先写图表Diagram,整个GoJS图形最外层就是一个Diagram图表对象:
var $ = go.GraphObject.make; // 用go.GraphObject.make来创建一个GoJS对象,如果$被占用了,可以使用其他名称 // 创建图表对象,绑定HTML中的DIV,初始化图表风格 var myDiagram = $(go.Diagram, "myDiagramDiv", // 这个名称"myDiagramDiv"必须同HTML中画布DIV的id一致 { initialContentAlignment: go.Spot.Center, // 居中显示内容 "undoManager.isEnabled": true, // 启用Ctrl-Z撤销和Ctrl-Y重做功能 layout: $(go.TreeLayout, // 树形排列布局 { angle: 90, // 父子节点之间竖排,即同水平线成90度直角 layerSpacing: 35 // 父子节点默认间距35像素 }) });
上面的代码定义了一个树形布局图表,也就是节点之间有父子关系,如果一个节点定义了父节点,就会有默认连线连接。如果不声明为树形布局,则任一连线需要指定的起始节点和末尾节点才行。
下一步写节点Node模板:
// 改节点模板里有两部分内部,一部分是图片,一部分是文字,水平排列 myDiagram.nodeTemplate = $(go.Node, "Horizontal", // 节点中的内容水平排列 { background: "#44CCFF" }, // 节点的背景色 // 定义图片尺寸,边间距,绑定图片地址,如果没指定图片地址则默认显示红色 $(go.Picture, { margin: 10, width: 50, height: 50, background: "red" }, new go.Binding("source")), // 节点中的图片地址绑定模型中的"source"属性 // 定义字体风格,默认显示"空白"字样,绑定文字内容 $(go.TextBlock, "空白", { margin: 12, stroke: "white", font: "bold 16px sans-serif" }, new go.Binding("text", "name")) // 节点中显示的文字绑定模型中的"name"属性 );
代码的解释都是在注释上,不多述了。Node模板可以定义多个,此时就要用”myDiagram.nodeTemplateMap.add()”方法来添加Node模板,而且每个Node模板需指定唯一的名称来区分。本例为了简洁只定义了一个Node模板。
然后,我们写连线Link模板:
// 该连线模板定义了连线的折线风格和线的尺寸颜色 myDiagram.linkTemplate = $(go.Link, { routing: go.Link.Orthogonal, corner: 5 }, // 风格:折线直角弯,弧度5像素 $(go.Shape, { strokeWidth: 3, stroke: "#840" }) // 线宽3像素,褐色 );
同Node一样,Link也可以定义多个,用”myDiagram.linkTemplateMap.add()”方法来添加Link模板。
最后,我们写模型Model。GoJS的模型就是一个JSon格式,所以很容易保存分发。其中有规定必须有的字段属性,也有自定义的属性,自定义属性就需要在刚才写Node或Link模板时声明绑定。
var model = $(go.TreeModel); // 声明模型变量的类型是树形 // 模型内容,三层树,6个节点,每个节点1张图片配1个名字 model.nodeDataArray = [ // 树形模型必须有"key"和"parent"的字段名, // 你还可以添加任何需要的其他字段,只需要在Node节点模板里绑定就行 { key: "1", name: "小花", source: "img/cat1.png" }, { key: "2", parent: "1", name: "小黄", source: "img/cat2.png" }, { key: "3", parent: "1", name: "小灰", source: "img/cat3.png" }, { key: "4", parent: "3", name: "小白", source: "img/cat4.png" }, { key: "5", parent: "3", name: "小喵", source: "img/cat5.png" }, { key: "6", parent: "2", name: "小黑", source: "img/cat6.png" }, { /* 空节点数据 */ } // 空节点会使用节点模板中定义的默认值,如本例红色图片,"空白"字样 ]; myDiagram.model = model; // 将模型绑定到图表上
因为要确保JS在HTML文档加载完执行,所以,上面的所有JS代码需放在”window.onload”事件内。朋友们可以把上面的代码保存下来试试。记得找6张可爱的图片,你将看到类似于下面的内容。
图表中的图形节点可以任意拖拉,连线也会相应的自动移动,这就是GoJS帮我们省力的地方。
另外,我基于GoJS官方范例,改了一个设计流程图的工具,为了简化代码,去除了其他jQuery和Bootstrap库。现在把代码贴在下面,说明都写在注释上了。感兴趣的朋友们,可以贴到文件里跑跑试试。
HTML部分
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>流程图设计器</title> <meta name="description" content="交互式流程图设计器" /> <meta charset="UTF-8"> <script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/1.8.36/go.js"></script> <script src="main.js"></script> </head> <body> <div id="sample" style="width: 1280px; margin: 0 auto;" > <div style="width: 100%; display: flex; justify-content: space-between"> <div id="myPaletteDiv" style="width: 120px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div> <div id="myDiagramDiv" style="flex-grow: 1; height: 750px; border: solid 1px black"></div> </div> <button id="saveButton">保存</button> <button id="loadButton">加载</button> 图模型保存为JSon格式: <textarea id="mySavedModel" style="width:100%;height:300px"> </textarea> <button id="printButton">用SVG打印</button> </div> </body> </html>
JS部分,即HTML上”main.js”的内容
window.onload = function() { var $ = go.GraphObject.make; // 创建画布 var myDiagram = $(go.Diagram, "myDiagramDiv", // 必须与Div元素的id属性一致 { initialContentAlignment: go.Spot.Center, // 居中显示内容 "undoManager.isEnabled": true, // 启用Ctrl-Z和Ctrl-Y撤销重做功能 allowDrop: true, // 是否允许从Palette面板拖入元素 "LinkDrawn": showLinkLabel, // 每次画线后调用的事件:为条件连线加上标签,该方法再后面定义 "LinkRelinked": showLinkLabel, // 每次重画线后调用的事件:同上LinkDrawn scrollsPageOnFocus: false // 图选中时页面不会滚动 }); // 当图有改动时,在页面标题后加*,且启动保存按钮 myDiagram.addDiagramListener("Modified", function(e) { var button = document.getElementById("SaveButton"); if (button) { button.disabled = !myDiagram.isModified; } var idx = document.title.indexOf("*"); if (myDiagram.isModified) { if (idx < 0) document.title += "*"; } else { if (idx >= 0) document.title = document.title.substr(0, idx); } }); // 设置节点位置风格,并与模型"loc"属性绑定,该方法会在初始化各种节点模板时使用 function nodeStyle() { return [ // 将节点位置信息 Node.location 同节点模型数据中 "loc" 属性绑定: // 节点位置信息从 节点模型 "loc" 属性获取, 并由静态方法 Point.parse 解析. // 如果节点位置改变了, 会自动更新节点模型中"loc"属性, 并由 Point.stringify 方法转化为字符串 new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify), { // 节点位置 Node.location 定位在节点的中心 locationSpot: go.Spot.Center } ]; } // 创建"port"方法,"port"是一个透明的长方形细长图块,在每个节点的四个边界上,如果鼠标移到节点某个边界上,它会高亮 // "name": "port" ID,即GraphObject.portId, // "align": 决定"port" 属于节点4条边的哪条 // "spot": 控制连线连入/连出的位置,如go.Spot.Top指, go.Spot.TopSide // "output" / "input": 布尔型,指定是否允许连线从此"port"连入或连出 function makePort(name, align, spot, output, input) { // 如果是上、下边界,则是水平"port" var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom); return $(go.Shape, { fill: "transparent", // 默认透明不现实 strokeWidth: 0, // 无边框 width: horizontal ? NaN : 8, // 垂直"port"则8像素宽 height: !horizontal ? NaN : 8, // 水平"port"则8像素 alignment: align, // 同其节点对齐 stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical), // 自动同其节点一同伸缩 portId: name, // 声明ID fromSpot: spot, // 声明连线头连出此"port"的位置 fromLinkable: output, // 布尔型,是否允许连线从此"port"连出 toSpot: spot, // 声明连线尾连入此"port"的位置 toLinkable: input, // 布尔型,是否允许连线从此"port"连出 cursor: "pointer", // 鼠标由指针改为手指,表示此处可点击生成连线 mouseEnter: function(e, port) { // 鼠标移到"port"位置后,高亮 if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)"; }, mouseLeave: function(e, port) { // 鼠标移出"port"位置后,透明 port.fill = "transparent"; } }); } // 图形上的文字风格 function textStyle() { return { font: "11pt Helvetica, Arial, sans-serif", stroke: "whitesmoke" } } // 定义步骤(默认类型)节点的模板 myDiagram.nodeTemplateMap.add("", // 默认类型 $(go.Node, "Table", nodeStyle(), // 步骤节点是一个包含可编辑文字块的长方形图块 $(go.Panel, "Auto", $(go.Shape, "Rectangle", { fill: "#00A9C9", strokeWidth: 0 }, new go.Binding("figure", "figure")), $(go.TextBlock, textStyle(), { margin: 8, maxSize: new go.Size(160, NaN), wrap: go.TextBlock.WrapFit, // 尺寸自适应 editable: true // 文字可编辑 }, new go.Binding("text").makeTwoWay()) // 双向绑定模型中"text"属性 ), // 上、左、右可以入,左、右、下可以出 // "Top"表示中心,"TopSide"表示上方任一位置,自动选择 makePort("T", go.Spot.Top, go.Spot.TopSide, false, true), makePort("L", go.Spot.Left, go.Spot.LeftSide, true, true), makePort("R", go.Spot.Right, go.Spot.RightSide, true, true), makePort("B", go.Spot.Bottom, go.Spot.BottomSide, true, false) )); // 定义条件节点的模板 myDiagram.nodeTemplateMap.add("Conditional", $(go.Node, "Table", nodeStyle(), // 条件节点是一个包含可编辑文字块的菱形图块 $(go.Panel, "Auto", $(go.Shape, "Diamond", { fill: "#00A9C9", strokeWidth: 0 }, new go.Binding("figure", "figure")), $(go.TextBlock, textStyle(), { margin: 8, maxSize: new go.Size(160, NaN), wrap: go.TextBlock.WrapFit, // 尺寸自适应 editable: true // 文字可编辑 }, new go.Binding("text").makeTwoWay()) ), // 上、左、右可以入,左、右、下可以出 makePort("T", go.Spot.Top, go.Spot.Top, false, true), makePort("L", go.Spot.Left, go.Spot.Left, true, true), makePort("R", go.Spot.Right, go.Spot.Right, true, true), makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false) ) ); // 定义开始节点的模板 myDiagram.nodeTemplateMap.add("Start", $(go.Node, "Table", nodeStyle(), // 开始节点是一个圆形图块,文字不可编辑 $(go.Panel, "Auto", $(go.Shape, "Circle", { minSize: new go.Size(40, 40), fill: "#79C900", strokeWidth: 0 }), $(go.TextBlock, "Start", textStyle(), new go.Binding("text")) ), // 左、右、下可以出,但都不可入 makePort("L", go.Spot.Left, go.Spot.Left, true, false), makePort("R", go.Spot.Right, go.Spot.Right, true, false), makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false) ) ); // 定义结束节点的模板 myDiagram.nodeTemplateMap.add("End", $(go.Node, "Table", nodeStyle(), // 结束节点是一个圆形图块,文字不可编辑 $(go.Panel, "Auto", $(go.Shape, "Circle", { minSize: new go.Size(40, 40), fill: "#DC3C00", strokeWidth: 0 }), $(go.TextBlock, "End", textStyle(), new go.Binding("text")) ), // 上、左、右可以入,但都不可出 makePort("T", go.Spot.Top, go.Spot.Top, false, true), makePort("L", go.Spot.Left, go.Spot.Left, false, true), makePort("R", go.Spot.Right, go.Spot.Right, false, true) ) ); // 定义注释节点的模板 myDiagram.nodeTemplateMap.add("Comment", $(go.Node, "Auto", nodeStyle(), // 注释节点是一个包含可编辑文字块的文件图块 $(go.Shape, "File", { fill: "#EFFAB4", strokeWidth: 0 }), $(go.TextBlock, textStyle(), { margin: 5, maxSize: new go.Size(200, NaN), wrap: go.TextBlock.WrapFit, // 尺寸自适应 textAlign: "center", editable: true, // 文字可编辑 font: "bold 12pt Helvetica, Arial, sans-serif", stroke: '#454545' }, new go.Binding("text").makeTwoWay()) // 不支持连线入和出 )); // 初始化连接线的模板 myDiagram.linkTemplate = $(go.Link, // 所有连接线 { routing: go.Link.AvoidsNodes, // 连接线避开节点 curve: go.Link.JumpOver, corner: 5, toShortLength: 4, // 直角弧度,箭头弧度 relinkableFrom: true, // 允许连线头重设 relinkableTo: true, // 允许连线尾重设 reshapable: true, // 允许线形修改 resegmentable: true, // 允许连线分割(折线)修改 // 鼠标移到连线上后高亮 mouseEnter: function(e, link) { link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"; }, mouseLeave: function(e, link) { link.findObject("HIGHLIGHT").stroke = "transparent"; }, selectionAdorned: false }, new go.Binding("points").makeTwoWay(), // 双向绑定模型中"points"数组属性 $(go.Shape, // 隐藏的连线形状,8个像素粗细,当鼠标移上后显示 { isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" } ), $(go.Shape, // 连线规格(颜色,选中/非选中,粗细) { isPanelMain: true, stroke: "gray", strokeWidth: 2 }, new go.Binding("stroke", "isSelected", function(sel) { return sel ? "dodgerblue" : "gray"; }).ofObject() ), $(go.Shape, // 箭头规格 { toArrow: "standard", strokeWidth: 0, fill: "gray"} ), $(go.Panel, "Auto", // 连线标签,默认不显示 { visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5}, new go.Binding("visible", "visible").makeTwoWay(), // 双向绑定模型中"visible"属性 $(go.Shape, "RoundedRectangle", // 连线中显示的标签形状 { fill: "#F8F8F8", strokeWidth: 0 }), $(go.TextBlock, "是", // 连线中显示的默认标签文字 { textAlign: "center", font: "10pt helvetica, arial, sans-serif", stroke: "#333333", editable: true }, new go.Binding("text").makeTwoWay()) // 双向绑定模型中"text"属性 ) ); // 此事件方法由整个画板的LinkDrawn和LinkRelinked事件触发 // 如果连线是从”conditional"条件节点出发,则将连线上的标签显示出来 function showLinkLabel(e) { var label = e.subject.findObject("LABEL"); if (label !== null) { label.visible = (e.subject.fromNode.data.category === "Conditional"); } } // 临时的连线(还在画图中),包括重连的连线,都保持直角 myDiagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal; myDiagram.toolManager.relinkingTool.temporaryLink.routing = go.Link.Orthogonal; // 在图形页面的左边初始化图例Palette面板 myPalette = $(go.Palette, "myPaletteDiv", // 必须同HTML中Div元素id一致 { scrollsPageOnFocus: false, // 图选中时页面不会滚动 nodeTemplateMap: myDiagram.nodeTemplateMap, // 同myDiagram公用一种node节点模板 model: new go.GraphLinksModel([ // 初始化Palette面板里的内容 { category: "Start", text: "开始" }, { text: "步骤1" }, { category: "Conditional", text: "条件1" }, { category: "End", text: "结束" }, { category: "Comment", text: "注释" } ]) }); // 初始化模型范例 myDiagram.model = go.Model.fromJson( { "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Start", "text":"开始", "key":1, "loc":"88 37"}, {"text":"烧开水", "key":2, "loc":"88 114"}, {"category":"Conditional", "text":"水是否烧开", "key":3, "loc":"88 210"}, {"text":"下面条", "key":4, "loc":"87 307"}, {"text":"等待3分钟", "key":5, "loc":"87 375"}, {"category":"End", "text":"结束", "key":6, "loc":"87 445"} ], "linkDataArray": [ {"from":2, "to":3, "fromPort":"B", "toPort":"T"}, {"from":3, "to":2, "fromPort":"R", "toPort":"R", "visible":true, "text":"否"}, {"from":1, "to":2, "fromPort":"B", "toPort":"T"}, {"from":3, "to":4, "fromPort":"B", "toPort":"T", "visible":true}, {"from":4, "to":5, "fromPort":"B", "toPort":"T"}, {"from":5, "to":6, "fromPort":"B", "toPort":"T"} ]} ); document.getElementById("mySavedModel").value = myDiagram.model.toJson(); // 将 go 模型以JSon格式保存在文本框内 document.getElementById("saveButton").addEventListener("click", function() { document.getElementById("mySavedModel").value = myDiagram.model.toJson(); myDiagram.isModified = false; }); // 读取文本框内JSon格式的内容,并转化为gojs模型 document.getElementById("loadButton").addEventListener("click", function() { myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value); }); // 在新窗口中将图形转化为SVG,并分页打印 document.getElementById("printButton").addEventListener("click", function() { var svgWindow = window.open(); if (!svgWindow) return; // 创建新窗口失败 var printSize = new go.Size(700, 960); var bnds = myDiagram.documentBounds; var x = bnds.x; var y = bnds.y; while (y < bnds.bottom) { while (x < bnds.right) { var svg = myDiagram.makeSVG({ scale: 1.0, position: new go.Point(x, y), size: printSize }); svgWindow.document.body.appendChild(svg); x += printSize.width; } x = bnds.x; y += printSize.height; } setTimeout(function() { svgWindow.print(); }, 1); }); } // windows.onload
跑下来,大家应该可以看到如下内容。
本文中的示例代码,可以在这里下载。
更详细的开发API文档可以在 GoJS官方网站 中找到。
以上所述就是小编给大家介绍的《前端交互式图表绘制库GoJS介绍》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- EnterpriseDB: 交互式命令行安装
- Go ssh 交互式执行命令
- 哭了!好美!交互式《几何原本》再现江湖
- CVPR 2018:一种交互式纹理迁移通用框架
- Cockpit 182 发布,交互式服务器管理界面
- Node.js REPL(交互式解释器)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Base64 编码/解码
Base64 编码/解码
Markdown 在线编辑器
Markdown 在线编辑器