GoJS-FlowChart样例代码分析

var $ = go.GraphObject.make;  // 别名,方便使用

    myDiagram =
      $(go.Diagram, "myDiagramDiv",  // 通过id指定画布绑定的div
        {
          "LinkDrawn": showLinkLabel,  // 监听LinkDrawn事件,其回调函数是showLinkLabel
          "LinkRelinked": showLinkLabel, // 同上,监听LinkRelinked事件
          "undoManager.isEnabled": true  // 允许 undo & redo
        });

        以上定义了图和图全局的属性、事件。最外层是一个go.Diagram对象,其结构为:每一个Diagram对象都由一些Layer对象组成,而一个Layer对象包含了数个Part对象(Part对象包括Node对象和Link对象等)。每一个Part对象由数个GraphObject(例如TextBlockShapePanel对象)组成,Panel对象可以包含更多的GraphObject对象。更具体的说明:Class Diagram | GoJS

        如下再给出一个Diagram对象的定义,读者品一品不同层级嵌套的参数表示的,分别是什么类:

var $ = go.GraphObject.make; 

myDiagram.nodeTemplate =
$(go.Node, "Auto",
 $(go.Shape, "RoundedRectangle",
   new go.Binding("fill", "color")),
 $(go.TextBlock,
   { margin: 3 },
   new go.Binding("text", "key"))
);

myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", color: "lightblue" },
{ key: "Beta", color: "orange" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Beta", to: "Beta" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);

接下来继续解析样例代码:

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);
      }
    });

这段代码作用是监听GoJS中的事件,改变页面的title,表示一下当前页面的图有没有改过,就是表达一下GoJS有自定义的事件系统。

接下来是一些辅助函数,帮助定义节点模版:

function nodeStyle() {
      return [
        // 将节点的location属性与数据对象的loc属性双向绑定
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        {
          // location属性指定的是是节点中央的位置
          locationSpot: go.Spot.Center
        }
      ];
    }

    // 定义一个port
    // "name"属性 被用作GraphObject.portId,
    // "align"属性指定port在节点的哪个位置,
    // "spot"属性控制连线如何连接到port并控制port是否沿着节点的边延伸,
    // "output"和"input"参数控制用户是否能绘制出去/进来的连线
    function makePort(name, align, spot, output, input) {
      var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
      // port就是一个在节点边上延伸的小矩形
      return $(go.Shape,
        {
          fill: "transparent",  // 一开始透明
          strokeWidth: 0,  // 无边框
          width: horizontal ? NaN : 8,  // 若无水平延伸,宽度为8
          height: !horizontal ? NaN : 8,  // 若无垂直延伸,高度为8
          alignment: align,  // port在节点上的位置
          stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical), // 拉伸方向
          portId: name,  // 声明这个对象是个 "port"
          fromSpot: spot,  // 声明连线可以连到哪里
          fromLinkable: output,  // 声明用户是否可以从这里连线
          toSpot: spot,  // 连线是否可以连接到这里
          toLinkable: input,  // 用户是否可以连线到这里
          cursor: "pointer",  // 鼠标移入会有变化,标识这儿可以连线
          mouseEnter: function(e, port) {  // 鼠标移入
            if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
          },
          mouseLeave: function(e, port) {
            port.fill = "transparent";
          }
        });
    }

    function textStyle() {
      return {
        font: "bold 11pt Lato, Helvetica, Arial, sans-serif",
        stroke: "#F8F8F8"
      }
    }
  1. go.Binding():给图中节点和连线绑定数据,go.Point.parse是定义的一个转换数据格式的函数,将字符串location解析为节点的loc属性,也就是定位属性可以用的数据格式。makeTwoWay()则实现了双向绑定。具体参考:Class Binding | GoJS
  2. 上面三个都是辅助函数,在定义节点模版时会用到

接下来定义了节点模版,可以定义多种节点:

myDiagram.nodeTemplateMap.add("",  // 默认类别
      $(go.Node, "Table", nodeStyle(),
        // 主对象是panel,包括一个包裹了文本内容的矩形
        $(go.Panel, "Auto",
          $(go.Shape, "Rectangle",
            { fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5 },
            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())
        ),
        // 定义了四个port,分布在四条边
        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: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5 },
            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, "Spot",
          $(go.Shape, "Circle",
            { desiredSize: new go.Size(70, 70), fill: "#282c34", stroke: "#09d3ac", strokeWidth: 3.5 }),
          $(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, "Spot",
          $(go.Shape, "Circle",
            { desiredSize: new go.Size(60, 60), fill: "#282c34", stroke: "#DC3C00", strokeWidth: 3.5 }),
          $(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)
      ));

    // 在 ../extensions/Figures.js 中,自定义了一种图形:
    go.Shape.defineFigureGenerator("File", function(shape, w, h) {
      var geo = new go.Geometry();
      var fig = new go.PathFigure(0, 0, true); // starting point
      geo.add(fig);
      fig.add(new go.PathSegment(go.PathSegment.Line, .75 * w, 0));
      fig.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
      fig.add(new go.PathSegment(go.PathSegment.Line, w, h));
      fig.add(new go.PathSegment(go.PathSegment.Line, 0, h).close());
      var fig2 = new go.PathFigure(.75 * w, 0, false);
      geo.add(fig2);
      // The Fold
      fig2.add(new go.PathSegment(go.PathSegment.Line, .75 * w, .25 * h));
      fig2.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
      geo.spot1 = new go.Spot(0, .25);
      geo.spot2 = go.Spot.BottomRight;
      return geo;
    });

    myDiagram.nodeTemplateMap.add("Comment",
      $(go.Node, "Auto", nodeStyle(),
        $(go.Shape, "File",
          { fill: "#282c34", stroke: "#DEE0A3", strokeWidth: 3 }),
        $(go.TextBlock, textStyle(),
          {
            margin: 8,
            maxSize: new go.Size(200, NaN),
            wrap: go.TextBlock.WrapFit,
            textAlign: "center",
            editable: true
          },
          new go.Binding("text").makeTwoWay())
      ));

其中,defineFigureGenerator()自定义了一种图形,具体可参考:图形形状 | GoJS

myDiagram.linkTemplate =
      $(go.Link, // 整个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(),
        $(go.Shape,  // 高亮的Shape样式
          { 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(),
          $(go.Shape, "RoundedRectangle",  // 标签形状
            { fill: "#F8F8F8", strokeWidth: 0 }),
          $(go.TextBlock, "Yes",  // 标签
            {
              textAlign: "center",
              font: "10pt helvetica, arial, sans-serif",
              stroke: "#333333",
              editable: true
            },
            new go.Binding("text").makeTwoWay())
        )
      );

以上定义了连线模版,实际上,在本图中有两种连线:带标签和不带标签的,从菱形组件出发的连线是带标签的。这两种线被统一定义在一个连线模版中,由visible属性控制是否显示标签。

接下来就是一些常规的辅助函数和输入输出了,不做赘述:

    // 根据出发节点类型控制标签隐藏
    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;

    load();  // 加载数据到图中

    // 初始化左侧“调色板”,即一些备选图形
    myPalette =
      $(go.Palette, "myPaletteDiv",  // 制定一个div的id
        {
          // 自定义简单动画
          "animationManager.initialAnimationStyle": go.AnimationManager.None,
          "InitialAnimationStarting": animateFadeDown,

          nodeTemplateMap: myDiagram.nodeTemplateMap,  // 共享在主图中定义好的集中节点模版
          model: new go.GraphLinksModel([  // 指定备选图形
            { category: "Start", text: "Start" },
            { text: "Step" },
            { category: "Conditional", text: "???" },
            { category: "End", text: "End" },
            { category: "Comment", text: "Comment" }
          ])
        });

    // 自定义的简单动画效果
    function animateFadeDown(e) {
      var diagram = e.diagram;
      var animation = new go.Animation();
      animation.isViewportUnconstrained = true;
      animation.easing = go.Animation.EaseOutExpo;
      animation.duration = 900;
      animation.add(diagram, 'position', diagram.position.copy().offset(0, 200), diagram.position);
      animation.add(diagram, 'opacity', 0, 1);
      animation.start();
    }

  }


  // 图的数据就是一个json串,保存这个json串即可
  function save() {
    document.getElementById("mySavedModel").value = myDiagram.model.toJson();
    myDiagram.isModified = false;
  }
  function load() {
    myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值