GoJS绘图基础探索笔记

最近又因为工作需要做了点GoJS研究如下
发现这个东西非常有用简直是可视化神器
官网教程很清楚但是根本没有中文Api (╯‵□′)╯︵┻━┻

Reference:
https://gojs.net/latest/learn/
https://segmentfault.com/a/1190000005182494

GoJS是一个可视化JavaScript库,用于浏览器中创建交互图形。
GoJS不依赖于任何JS库或框架(例如bootstrap、jquery等),可与任何HTML或JS框架配合工作,甚至可以不用框架。

Part1:基础介绍及代码构建

简单地讲,GoJS的绘图基于Html5的Canvas元素。一个Gojs文件基本构成包括画布,数据模型,模型内节点等。

Step1:创建画布(Html)

<div id="stDiagram2" style="width:800px; height:300px; background-color: #DAE4E4;margin-bottom: 10px;"></div>

需给定id,长宽。Gojs会在该div中创建canvas。

Step2:初始化画布(Js)

var myDiagram = new go.Diagram("stDiagram");

var $ = go.GraphObject.make;
var myDiagram =
$(go.Diagram, "stDiagram",
{   ... });

其中常用属性:

  • initialContentAlignment: go.Spot.Center, // 画布内居中显示
  • "grid.visible": true, //画布上面是否出现网格
  • allowZoom: false, //画布是否可以缩放
    更多初始化画布的参数请参考官方api。

注意:
GraphObject.make为GoJS定义的静态函数,用于生成对象并赋予其类。
GraphObject.make的第一个参数必须是一个类类型。通常是一个字符串,可以设定TextBlock.text,Shape.figure,Picture.source或Panel.type。

Step3:定义模型数据(Js)

var myModel = $(go.Model);
 // model中的数据每一个js对象都代表着一个相应的模型图中的元素
myModel.nodeDataArray = [
      { key: "Amos"},
      { key: "Saint"},
];
myDiagram.model = myModel; //将模型数据绑定到画布图上
基本图.png

Gojs中的数据模型分三种。

  • Model:最基本的(不带连线,如上面的例子)
  • GraphLinksModel :连线图
  • TreeModel:树形图

GraphLinksModel中用model.linkDataArray为node节点连线保存数据模型信息。在连线图中,可以设定节点的连接方式。

var myModel = $(go.GraphLinksModel);
myModel.nodeDataArray = [
    { key: "Amos"},
    { key: "Saint"}
];
myModel.linkDataArray = [
    { from: "Amos", to: "Saint" }
];
myDiagram.model = myModel;
连线图.png

Step4:定义node节点(Js)

myDiagram.nodeTemplate =
$(go.Node, "Horizontal",
    {
          locationSpot: go.Spot.Center, // 节点内居中显示
          background: "indigo" //每个节点背景色
    },
        ...
);

在...中,可以加入$(go.TextBlock,{ ... }), $(go.Picture,{ ... }), $(go.Shape,{ ... }), $(go.Panel,{ ... }),四种节点。分别为文本快(可编辑),图片,图形,面板(来保存其他Node的集合) 。

Shape包括Rectangle(矩形)、RoundedRectangle(圆角矩形),Ellipse(椭圆形),Triangle(三角形),Diamond(菱形),Circle(圆形)等可以直接调用的形状。(GoJS默认提供了很多形状然而Api写的很一般,需要自己去官方文档进行对照O.O)

$(go.Shape,
    "RoundedRectangle",
        { margin: 10, width: 50, height: 50, figure: "Club",
          stroke: "#C2185B", fill: "#F48FB1", strokeWidth: 3
        },
        new go.Binding("figure", "fig")),

Picture比较简单,根据url获取图片。

$(go.Picture,
    { margin: 10, width: 50, height: 50, background: "#44CCFF" },
    new go.Binding("source", "url")),

TextBlock可设置宽高换行,以及可编辑性。

$(go.TextBlock,
        "default text",
        { margin: 12, stroke: "white", font: "14px sans-serif" },
        new go.Binding("text", "name"))

Panel为节点合集,见下一小节。

对应数据模型:
在数据模型中,通过设定上段代码Binding的属性,对节点进行设置。

var myModel = $(go.Model);
myModel.nodeDataArray = [
    { fig:"Spade", url:"amos.jpg", name: "Amos"},
    { fig:"Heart", url:"st.jpg", name: "Saint"},
];
myDiagram.model = myModel;
有shape,picture和textblock的节点.png

Step5:增加node节点(Js)
通过Diagram.add可以在模型中增加node节点。

myDiagram.add($(
    go.Part, 'Horizontal',
    $(go.TextBlock,
        { text: '一个文本块'}
    ),
    $(go.TextBlock,
        { text: '一个有颜色的文本块', background: '#1ad', stroke: '#FFF', width:150, height:20, textAlign: 'center', alignment: go.Spot.Center, margin:10 }
    ),
    $(go.TextBlock,
        { text: '一个可编辑文本块', background: '#FFF', width:150, height:20, alignment: go.Spot.Center, textAlign: 'center', editable: true, margin:10 }
    ),
));
添加文本节点.png

Part2:面板Panel

面板中可以包含自己的node元素。每个面板建立自己的坐标系。面板有很多种类,诸如:

  • Panel.Position
  • Panel.Vertical
  • Panel.Horizo​​ntal
  • Panel.Auto
  • Panel.Spot
  • Panel.Table
  • Panel.Viewbox
  • Panel.Link
  • Panel.Grid

常用的有位置面板Panel.Position,纵向面板Panel.Vertical,横向面板Panel.Horizo​​ntal,表面板Panel.Table。

Panel.Position:
每个元素的位置是由position属性指定,默认定位在(0,0)左上角。

myDiagram.add($(
    go.Part,
    go.Panel.Position,
    {
        background: '#eee',
        //position: new go.Point(0, 0), //定位面板相对diagram的位置
    },
    $(go.TextBlock,
        {text: '0, 0', background: '#6bc1ff' }
    ),
    $(go.TextBlock,
        {text: '100, 100', background: '#6bc1ff', position: new go.Point(100, 100) }
    ),
    $(go.Shape,
    {width: 50,height: 50,stroke: "#3385ff",fill: '#6bc1ff', position: new go.Point(100, 0) }
    ),
));
位置面板.png

Panel.Vertical:
面板的所有元素垂直从上到下排列。
Panel.Horizo​​ntal:
类似纵向面板,排列不同。
垂直和水平两个面板支持Panel.defaultAlignment和Panel.defaultStretch属性,可以不必设置每个元素的对齐方式。

go.Panel.Horizontal,
{
    background: '#eee', defaultAlignment: go.Spot.Bottom
},
底部对齐的垂直面板.png

Panel.Table:
表格面板中具有GraphObject.row和GraphObject.column属性,面板会按照节点的行和列确定大小。
row和column的计数从0开始。

go.Panel.Table,
{
    //position: new go.Point(0, 0),
    background: '#eee',
},
$(go.TextBlock,
    {text: '一行一列', row: 0, column: 0, margin: 2, background: '#6bc1ff'}
),
表面版.png

当设置了行或列的长宽时,可以通过设置alignment: go.Spot.Left/Right/Center来设定表格内元素对齐方式。

$(go.RowColumnDefinition,
    { column: 0, width: 200, background: '#eee', }
),
$(go.Shape,
    'RoundedRectangle',
    { stroke: "#3385ff", fill: '#6bc1ff', row: 0, column: 0, alignment: go.Spot.Left, }
),
不同对齐方式.png

通过columnSpan和rowSpan属性,可以合并单元格。

$(go.TextBlock,
    {text: '顶标题', row: 0, column: 0, columnSpan: 3, 
    stretch: go.GraphObject.Horizontal, margin: 2, background: '#6bc1ff'}
),
合并单元格.png

Part3:数据绑定

GraphObject中的各种属性都可以进行数据绑定。在第一节我们建立基本模型图时已经用了数据绑定功能。
通常,在Template(nodeTemplate和linkTemplate)中,定义go.Binding的属性,并在DataArray(nodeDataArray和linkDataArray)中设定对应值。

Template中:

myDiagram2.nodeTemplate =
$(go.Node, "Auto",
  new go.Binding('location', 'loc'),
  $(go.Shape,
    { figure: "RoundedRectangle", fill: "white" },
    new go.Binding('fill', 'color'),
    new go.Binding('stroke', 'stroke')),
  $(go.TextBlock,
    { margin: 5 },
    new go.Binding('text', 'text')),
);
myDiagram2.linkTemplate = 
$(go.Link,
    $(go.Shape,
        new go.Binding('stroke', 'stroke'),
        new go.Binding('strokeWidth', 'thick')
    ),
    $(go.Shape,
        { toArrow: 'OpenTriangle', fill: null}
    )
)

DataArray中:

myModel2.nodeDataArray = [
    { key: "Amos", text: 'Amos', color: '#ff7a7a', stroke: "#ff4141", loc: new go.Point(0, 0) },
    { key: "Saint", text: 'Saint', color: '#6bc1ff', stroke: "#3385ff", loc: new go.Point(100, 100)}
];
myModel2.linkDataArray = [
    { from: "Amos", to: "Saint", stroke: '#333', thick: 2 }
];

设定数据绑定时,可以通过转换函数的第三个参数绑定构造函数。

new go.Binding('location', 'loc', go.Point.parse),

此处,go.Point.parse为一组二维数组。

myModel2.nodeDataArray = [
    { key: "Amos", text: 'Amos', color: '#ff7a7a', stroke: "#ff4141", loc: '0, 0' },
    { key: "Saint", text: 'Saint', color: '#6bc1ff', stroke: "#3385ff", loc: '100, 100'}
];

Part4:事件触发(拖拽为例)

官方api:(PS,Gojs的官方api用的是monokai配色,好评)

https://gojs.net/latest/intro/events.html

Gojs具备三种基本事件:

  • 针对图表的“基础事件”DiagramEvent。
  • 处理鼠标键盘的“输入事件”InputEvents。
  • 以及改变元素属性的“更改事件”ChangedEvent。

Gojs提供各种函数处理图表的交互行为。

由于上一篇研究的是Html5拖拽交互行为,此处依旧以输入事件的拖拽为例。在这里将官方Planogram的例子进行简化,并按步骤生成。
最终效果为:


拖拽事件.png

针对拖拽,Gojs提供GraphObject.mouseDragEnter, GraphObject.mouseDragLeave, GraphObject.mouseDrop 三种操作。分别对应鼠标拖拽进入,离开,与释放。

Step1:创建画布(Html)
我们可以把拖拽事件看成将一些物品放置在桌子上。我们需要创建两个画布,第一个放置于左侧用来存放物品,第二个用于充当桌子。在这个例子中,我们设定了三类物品,四张桌子。
Html构成如下:

<div style="width: 150px;height:450px;float: left;border:1px solid #bef;">
    <div id="myPalette" style="width: 140px; height: 450px"></div>
</div>
<div id="stDiagram" style="float: left; width:800px; height:450px; border:1px solid #666; margin-bottom: 10px; margin-left: 20px;"></div>

Step2:初始化图表(JS)
我们将主图表以网格方式呈现,并在Diagram中定义allowDrop: true属性,使其可拖放。

var CellSize = new go.Size(50, 50);
var myDiagram =
$(go.Diagram, "stDiagram",
{
    //define grid size
    grid: $(go.Panel, "Grid",
      { gridCellSize: CellSize },
      $(go.Shape, "LineH", { stroke: "lightgray" }),
      $(go.Shape, "LineV", { stroke: "lightgray" })
    ),
    //allow drop on diagram
    allowDrop: true,
    //support grid snapping when dragging
    "draggingTool.isGridSnapEnabled": true,
    "draggingTool.gridSnapCellSpot": go.Spot.Center,
});

"draggingTool.isGridSnapEnabled": true,让拖放可以对齐网格进行。

网格.png

Step3:创建桌子和物品(JS)
在Template中,设定mouseDragEnter, mouseDragLeave, mouseDrop所触发的交互行为。
首先,对桌子所在的groupTemplate进行定义。以灰色的方形显示桌子。

    var groupFill = "rgba(128,128,128,0.2)";
    var groupStroke = "gray";
myDiagram.groupTemplate =
  $(go.Group,
    {
      layerName: "Background",
      resizable:  false,
      locationSpot: new go.Spot(0, 0, CellSize.width/2, CellSize.height/2)
    },
    //save/load the point that is the top-left corner of the node, not the location
    new go.Binding("position", "pos", go.Point.parse),

    $(go.Shape, "Rectangle",  // the rectangular shape around the members
      { name: "SHAPE",
        fill: groupFill,
        stroke: groupStroke,
      },
      new go.Binding("desiredSize", "size", go.Size.parse)),
  );
    myDiagram.model = new go.GraphLinksModel([
      { key: "G1", isGroup: true, pos: "0 0", size: "200 200" },
      { key: "G2", isGroup: true, pos: "200 0", size: "200 200" },
      { key: "G3", isGroup: true, pos: "0 200", size: "200 200" },
      { key: "G4", isGroup: true, pos: "200 200", size: "200 200" }
    ]);

此时生成4张桌子,并可进行拖拽。

桌子.png

接下来,通过设定nodeTemplate定义左侧物品模板。设定物品为圆角方块。

    myDiagram.nodeTemplate =
    $(go.Node, "Auto",
        {
            // set the node to center
            locationSpot: new go.Spot(0, 0, CellSize.width / 2, CellSize.height / 2),
            resizable: false,
        },
        new go.Binding("position", "loc", go.Point.parse),

        $(go.Shape,
            { name: "SHAPE", figure: "RoundedRectangle", fill: "white", minSize: CellSize},
            new go.Binding('fill', 'color'),
            new go.Binding('stroke', 'stroke')),
        $(go.TextBlock,
            { alignment: go.Spot.Center, font: 'bold 16px sans-serif' },
            new go.Binding('text', 'key')),
    );
    var green = '#B2FF59';
    var blue = '#81D4FA';
    var yellow = '#FFEB3B';

    var myModel = $(go.GraphLinksModel);
    myModel.nodeDataArray = [
      { key: "a", color: green },
      { key: "b", color: blue },
      { key: "c", color: yellow }
    ];
物品.png

Step4:设置交互(JS)
基本模板节点定义完毕后,我们开始通过GraphObject.mouseDragEnter, GraphObject.mouseDragLeave, GraphObject.mouseDrop进行简单的拖拽交互。
针对物品,改变物品位置时,可以通过updateTargetBindings();更新物品属性值。

myDiagram.nodeTemplate =
    $(go.Node, "Auto",
        {
            // set the node to center
            locationSpot: new go.Spot(0, 0, CellSize.width / 2, CellSize.height / 2),
            resizable: false,
            mouseDragLeave: function(e, node) {
                // assign new property values to the GraphObjects
                node.updateTargetBindings();
            },
        },
    );

对于桌子,为了更好地显示交互效果,我们设定,当有物品拖入桌子上方时改变桌子颜色。

    var dropFill = "rgba(128,255,255,0.2)";
    var dropStroke = "red";

在这里,定义一个新函数以判断是否需要改变颜色为高亮。后面,通过鼠标交互行为我们将触发这个函数。

    function highlightGroup(grp, show) {
      if (!grp) return;
      if (show) {  // check that the drop may really happen into the Group
          grp.isHighlighted = true;
          return;
      }
      grp.isHighlighted = false;
    }

当物品拖放至桌子时,改变颜色为高亮,拖放完毕后恢复原貌。

myDiagram.groupTemplate =
      $(go.Group,

        ……

        { // highlight group when dragover or dragdrop
          mouseDragEnter: function(e, grp, prev) { highlightGroup(grp, true); },
          mouseDragLeave: function(e, grp, next) { highlightGroup(grp, false); },
          mouseDrop: function(e, grp) {
            var ok = grp.addMembers(grp.diagram.selection, true);
            if (!ok) grp.diagram.currentTool.doCancel();
          }
        },

        ……

      new go.Binding("fill", "isHighlighted", function(h) { return h ? dropFill : groupFill; }).ofObject(),
      new go.Binding("stroke", "isHighlighted", function(h) { return h ? dropStroke: groupStroke; }).ofObject())
      );

至此,基本的拖拽效果设置完毕。


基本拖拽.png

这里的例子主要展示通过mouseDragEnter,mouseDragLeave,mouseDrop如何进行交互行为。还可以通过一些其他设置进行细化。请参考官方Planogram的代码。

总结

GoJS很强大,基本满足常见的各类图表生成与交互行为。只是目前没有找到完善的中文文档,有机会的话多尝试翻译几篇给大家。
就酱。
(´・ω・`)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容