可視化圖表庫--goJS


 

GoJSNorthwoods Software的產品。
Northwoods Software創立於1995年,專注於交互圖控件和類庫。旗下四款產品:

  • GoJS:用於在HTML上創建交互圖的純javaSCript庫
  • GoDiagram:用於WinForms的.NET圖控件。
  • GoXam:用於WPF/Silverlight的圖控件。( Silverlight是一個跨瀏覽器的、跨平台的插件, 與flash競爭的富客戶端技術)
  • JGo:用於Swing/SWT中創建交互圖的java庫。

 

GoJS可以做什么? 

GoJS是一個實現交互類可視化圖表(比如流程圖,樹圖,關系圖,力導圖,思維導圖等等)的JS庫。GoJS為用戶交互提供了許多高級功能,如拖放,刪除,復制和粘貼,撤銷與重做,文本編輯,工具提示,上下文菜單,自動布局,數據綁定和模型,事務狀態和撤銷管理,事件處理程序,命令以及用於自定義操作的可擴展工具系統等等。

// gojs可以繪制的圖形有

柱狀圖 barCharts 線形圖 canvases.html 比賽場次圖 beatPaths.html 蠟燭圖 candlestickCharts.html
類層次結構 classHierarchy.html 詮釋comments.html 概念圖conceptMap.html 動效圖 constantSize.html
決策樹 decisionTree.html 距離圖 distances.html 雙圓 doubleCircle.html 雙樹doubleTree.html doubleTreeJSON.html
流程圖 draggableLink.html 家族圖譜 familyTree.html 進度圖gantt.html 家族圖譜genogram.html 順序功能圖grafcet.html
溫度計 thermometer.html 時間軸 tabs.html 縱向面板 swimLanesVertical.html
橫向面板swimLanes.html 狀態圖stateChartIncremental.html 平面表格spreadsheet.html 線形圖 sparklineGraphs.html
監控圖 shopFloorMonitor.html 序列化函數sequentialFunction.html 序列化圖表sequenceDiagram.html
座位圖 seatingChart.html
桑基圖 sankey.html (桑基圖定義:它主要用來表示原材料、能量等如何從初始形式經過中間過程的加工、轉化到達最終形式,如下所示為最基本的事物狀態隨時間推移的變化)
圓形組roundedGroups.html 重組縮放regroupingScaled.html 雷達圖radialPartition.html 生成流程圖productionProcess.html
貨架圖planogram.html 管道圖pipes.html tab標簽頁 timeline.html

更多實例參考

https://gojs.net/latest/samples/index.html

 

為什么使用GoJS

用對象數據表達數據之間的邏輯關系,遠不如用圖像展示形象直觀 ,一圖勝千言,為了更直觀地表達信息,我們常常需要用圖形來展示數據以及邏輯關系。goJS圖表種類,交互行為豐富,自定義模板靈活,已經有非常多的圖表例子,支持復雜的模板定義和數據綁定,足夠解決實際業務中的常見圖表需求。

如何使用GoJS

step1  下載gojs 源碼  http://gojs.net/latest/site.zip 並在頁面中引用

step2  在頁面中創建goJS圖表容器,一定要設置寬高,否則圖形繪制不出來

step3 創建GraphObject圖表實例,(定義樣式,交互,布局,屬性)

step4 定義圖表屬性及事件 節點樣式事件 鏈路樣式及事件

step5 綁定圖表節點和鏈路數據, 渲染圖表

綜合節點模板、鏈接模板、TreeModel和Treelayout,就生成了一張家族圖譜。

 

gojs概念

圖表(Diagram)

所有GoJS的屬性和方法都在go這個命名空間下。所有GoJS的類名,例如Diagram、Node、Panel、 Shape、TextBlock也都使用go作為前綴,go.GraphObject.make來創建一個GoJS對象

GoJS圖表即最后看到的可視化視圖,它是由這些部分構成的:一個或多個可能有連接關系的、可能成組的節點。所有這些節點和鏈路聚集在相同或不同的層中,並呈現出一定的布局(開發者預定好的或GoJS自動布局)。

 

 Diagram(圖表屬性)

畫布圖表基本配置
畫布初始位置(定義之后就不能拖動了) initialContentAlignment: go.Spot.Center
初始坐標 initialPosition: new go.Point(0, 0)
禁止移動節點 allowMove:false
禁止復制 allowCopy: false
禁止刪除 allowDelete:false
禁止選中  allowSelect:false
禁止縮放 allowZoom: false
禁止撤銷和重做 "undoManager.isEnabled": false

禁止水平拖動畫布

禁止水平滾動條

 

allowHorizontalScroll: false

禁止垂直拖動畫布

禁止垂直滾動條

allowVerticalScroll: false
只讀 isReadOnly: true
畫布初始化動畫時間 "animationManager.duration": 600
禁止畫布初始化動畫 "animationManager.isEnabled": false
畫布比例 scale:1.5
畫布最小比例 minScale:1.2,
畫布最大比例  maxScale:2.0,
顯示網格  "grid.visible":true,
禁止鼠標拖動區域選中 "dragSelectingTool.isEnabled" : false, 
畫布邊距padding Margin padding:80或者new go.Margin(2, 0)或new go.Margin(1, 0, 0, 1)

 


 
 
 
 

  
  

 

 


 
 
 
 

 

 

 

 

 

更多設置參見 https://gojs.net/latest/api/symbols/Diagram.html

 

MV架構

GoJs使用model-view(MV架構)的模式,Models作為數據層來管理那些描述性的數據(JS數組對象),Diagrams則負責視圖層,將Nodes和Links的數據以可視化的方式渲染出來。Diagrams中的Nodes(節點)和Links(連線)呈現是由Model進行管理的。model和Diagrams實現了數據綁定,通過監聽Model數據,自動改變Nodes上的GraphObjects外觀和行為。Model數據對象是一個普通的JavaScript對象。我們編程操作只針對Model的數據層,而不是Diagrams的視圖層。可以按照業務需求在Models數據對象上面添加任意屬性,一般不需要修改Diagram的prototype(原型)和GraphObject(繪圖單元)的classes(類).

 

數據綁定是指從源對象中提取值並在目標對象上設置屬性。目標對象就是圖形對象(GraphObject),源對象是模型中保存的js數據對象。

使用模板和數據綁定簡化了存儲在模型數據中的信息,靈活性非常強。當然並不是所有的數據屬性都需要綁定使用

go.Binding VS 雙向數據綁定(Two-way data binding)

go.Binding綁定只將屬性的值從源數據轉移到目標對象。但有時我們希望能夠將GraphObject中的值傳輸回模型數據,使得模型數據與ui界面的圖標中的數據保持一致。這可以通過使用TwoWay 綁定,它可以完成從源數據到目標對象,以及從目標對象到源數據的值傳遞。

 

 

模型(Model)

每個圖表都有一個數據模型,用於保存開發者程序的數據。
模型描述了節點之間的連接關系和組成員關系。用模型 Model.nodeDataArray 為每個數據項創建一個節點或組, 用模型 GraphLinksModel.linkDataArray 為每個數據項創建一個鏈接。而且,我們可以為每個數據對象添加所需的任何屬性。

Models的種類

自定義的node模板讓我們的圖表看起來更美觀,如果要創建一個完整的關系圖,通過添加一些連線來表示這些獨立的節點之間的對應關系,同時這些節點能夠進行自動定位和排版。在我們的圖表里為了得到這些連線,基本的Model已經滿足不了需求。我們必須從GOJS中的支持連線的另外兩個Models里選擇,GraphLinksModel和TreeModel

GraphLinksModel中,除了model.nodeDataArray還有model.linkDataArray。它包含一個數組對象,通過”to”和”from”來描述沒一個連線。

GraphLinksModel允許兩個節點之間存在任何數量和任意方向的連線。比如A到B可以連10條線,B到A可以連3條以上反方向的線

TreeModel比GraphLinksModel更簡單,但是不能隨意的建利連接關系,就像2個節點之間有多條線,又或者有多個父級節點。

 

模板(Template)

模板聲明了每個節點或鏈路的外觀、位置和行為。

 Nodes模板

* Shape 預定義的或者自定義的幾何圖形 

 "Rectangle"--矩形, "RoundedRectangle"--圓角矩形, "Square"--正方形 "Ellipse"--橢圓  "Diamond"--菱形,  "Circle"--圓形   各種三角形 "TriangleRight", "TriangleDown", "TriangleLeft", "TriangleUp", "Triangle",

 "LineH", "LineV", "BarH", "BarV", "MinusLine", "PlusLine", "XLine"

* TextBlock 擁有各種各樣字體的文本(可編輯)
* Picture 圖片
* Panel 根據不同面板的類型,它可以包含其他位置或是尺寸不同的對象。(列如表格、 豎形列表和拉伸容器等)

TextBlocks不能包含圖片;Shapes不能包含文字。如果你想讓你的Node顯示文字,你必須使用TextBlocks。如果你想繪制一些幾何圖形,你就必須使用Shape。

myDiagram.nodeTemplate = $(
        go.Node,
        "Horizontal",
        { background: "#44CCFF" },
        $(
          go.Shape,
          "Rectangle",
          {
            portId: "",
            fromLinkable: true,
            toLinkable: true,
            cursor: "pointer",
            fill: "white",
            strokeWidth: 1
          },
          new go.Binding("figure"),
          new go.Binding("fill")
        ),
        $(go.Picture, { margin: 10, width: 50, height: 50, background: "red" }, new go.Binding("source"), new go.Binding("figure")),
        $(go.TextBlock, "Default Text", { margin: 12, stroke: "white", font: "bold 16px sans-serif" }, new go.Binding("text", "name"))
      );

 

所有的這些building block類都是由GraphObjects抽象對象衍生出來。因為GraphObject不是DOM元素,所以創建和修改它們對性能開銷不大。

 多個不同樣式的Node節點模板可以通過myDiagram.nodeTemplateMap.add(go.Node)添加

 myDiagram.nodeTemplateMap.add("Center",
        $(go.Node, "Spot", { selectable: false, isLayoutPositioned: false, // the Diagram.layout will not position this node  locationSpot: go.Spot.Center }, $(go.Shape, "Circle", { fill: radBrush, strokeWidth: 0, stroke: null, desiredSize: new go.Size(200, 200) }), // no outline $(go.TextBlock, "Arrowheads", { margin: 1, stroke: "white", font: "bold 14px sans-serif" }) ));
 myDiagram.model =
        $(go.GraphLinksModel,
          { // this gets copied automatically when there's a link data reference to a new node key
            // and is then added to the nodeDataArray
 archetypeNodeData: {}, // the node array starts with just the special Center node nodeDataArray: [{ category: "Center", key: "Center" }], // the link array was created above  linkDataArray: linkdata }); }

links模板

 接下來我們構造一個新的連線模板,在沒有為連線指定樣式類型的條件下,默認的連線樣式。

      myDiagram.linkTemplate =
        $(go.Link,  // the whole link panel
 { routing: go.Link.Normal }, $(go.Shape, // the link shape // the first element is assumed to be main element: as if isPanelMain were true { stroke: "gray", strokeWidth: 2 }), $(go.Shape, // the "from" arrowhead new go.Binding("fromArrow", "fromArrow"), { scale: 2, fill: "#D4B52C" }), $(go.Shape, // the "to" arrowhead new go.Binding("toArrow", "toArrow"), { scale: 2, fill: "#D4B52C" }), { click: showArrowInfo, toolTip: // define a tooltip for each link that displays its information $("ToolTip", $(go.TextBlock, { margin: 4 }, new go.Binding("text", "", infoString).ofObject()) ) } );

 

面板(Panel)

每個模板由GoJS中的面板Panel構成,面板本身作為一個圖形對象GraphObject,保存其他圖形對象作為它的元素,同時,面板需要負責圖形對象的尺寸、位置。
每個面板建立自己的坐標系,面板中的元素按順序繪制,從而確定了內部這些元素的z坐標。
面板有很多種類,比如 Panel.Position,Panel.Auto,Panel.Vertical,Panel.Horizontal ,Panel.Spot ,Panel.Table,Panel.Viewbox, Panel.Link,Panel.Grid等等。

最簡單的面板是“Position”(Panel.Position)。每個元素獲得其正常大小
每個元素的位置是由GraphObject.position屬性指定。如果沒有指定位置時,元件被定位在(0,0)。所有位置都是面板自己的坐標系中,而不是在圖表范圍的坐標系。位置可能包括負坐標。
面板的大小剛好足以容納所有元素。

Panel.Vertical 面板的所有面板元件的排列垂直從上到下

構成面板的圖形對象有Shapes、Pictures、TextBlocks  Placeholder,它們都有默認模板。

 

圖表布局

圖表(Diagram)在節點沒有指定坐標的時候,圖表會顯示一個用網格形式排列的默認布局。我們可以顯式的給每個節點分配一個位置來給組織排序來解決這個混亂的組織結構,更容易的解決方案是,我們會使用布局來自動排列位置。

設置了布局之后,會影響節點位置和鏈路屬性

 

常見的布局有:

網格布局 go.GridLayout

力導向布局 go.ForceDirectedLayout fdLayout.html

 

 

樹形布局 go.TreeLayout

徑向布局(需要引RadialLayout.js) RadialLayout

 

布局算法可以重寫

myDiagram =
$(go.Diagram, "myDiagramDiv", // 畫布定義
    {layout:$(go.GridLayout, //自動布局定義,設置為網格布局
              { comparer: go.GridLayout.smartComparer,//設置從小到大排序
                spacing: go.Size.parse("20 20"),//設置節點間隔
                comparer: function(a, b){ 
                    //重寫布局算法,根據其他屬性值重新增設置順序
                    var ay = a.data.type;
                    var by = b.data.type;
                    if(!!ay&&!!by){
                        if(ay > by) return -1;
                        if(ay < by) return 1;
                    }else if(!!ay){
                        return -1;
                    }else if(!!by){
                        return 1;
                    }    
                }
              });
    });

更多布局方式

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

畫布事件

利用繪圖過程中的一些DiagramListener可以完成更多的數據交互體驗和業務邏輯

節點生成事件 ExternalObjectsDropped
線生成事件 LinkDrawn
線重新連接事件 LinkRelinked
刪除后事件 SelectionDeleted
刪除前事件 SelectionDeleting 
節點移動事件 SelectionMoved
節點修改
Modified
選擇節點更改完成
ChangedSelection


 
 
 
 
 
 

 

 

添加圖表事件的兩種方式

第一種在創建Diagram時注冊對應的事件的監聽

myDiagram = goObj(go.Diagram, "myDiagramDiv",  
{
  initialContentAlignment: go.Spot.Center,
  allowDrop: true, 
  "LinkDrawn": showLinkLabel,  
  "LinkRelinked": showLinkLabel,
  "animationManager.duration": 800, 
  "undoManager.isEnabled": true  
});
function showLinkLabel(e) {
  var label = e.subject.findObject("LABEL");
  if (label !== null) label.visible = (e.subject.fromNode.data.figure === "RoundedRectangle");
}

第二種在創建Diagram時完成后監聽對應的事件

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

});

 

事件應用舉例: 約束編程--如果是孤立節點則刪除 

      myDiagram.addDiagramListener("ExternalObjectsDropped", function(e) {
        var newnode = e.diagram.selection.first(); if (newnode.linksConnected.count === 0) { // when the selection is dropped but not hooked up to the rest of the graph, delete it  e.diagram.commandHandler.deleteSelection(); } });

 

更多事件參見 https://gojs.net/latest/api/symbols/DiagramEvent.html

 畫布工具欄

結合undoManager創建撤銷重做刪除的工具欄

 $('#deletePart').click(function(){
       if("undefined" == typeof myDiagram){
              result_prompt(0, "瀏覽器不兼容此功能,請使用高版本谷歌瀏覽器!");
              return false;
       }
       myDiagram.remove(Select_Port);
  });

  $('#undo-buttun').click(function(){
   if("undefined" == typeof myDiagram){
          result_prompt(0, "瀏覽器不兼容此功能,請使用高版本谷歌瀏覽器!");
          return false;
   }
   myDiagram.undoManager.undo();
  });

  $('#redo-buttun').click(function(){
   if("undefined" == typeof myDiagram){
          result_prompt(0, "瀏覽器不兼容此功能,請使用高版本谷歌瀏覽器!");
          return false;
   }
   myDiagram.undoManager.redo();
  });

 

項目中用到的幾個功能點實現

1.鼠標右鍵菜單

在myDiagram.nodeTemplate 下配置節點鼠標右鍵菜單
 {
          contextMenu: $(go.Adornment, "Vertical", new go.Binding("itemArray", "commands"), {
            itemTemplate: $(
              "ContextMenuButton",
              $(go.Shape, { figure: "RoundedRectangle", fill: "transparent", width: 40, height: 24, stroke: "gray", strokeWidth: 1, scale: 1.0, areaBackground: "transparent" }),
              $(go.TextBlock, { stroke: "deepskyblue", height: 24, width: 40, margin: 0, font: "bold 12px serif", textAlign: "center", verticalAlignment: go.Spot.Center }, new go.Binding("text")),
              {
                click: function(e, button) {
                  if (myDiagram.isReadOnly) return;
                  var cmd = button.data;
                  var nodedata = button.part.adornedPart.data;
                  // console.log(nodedata);
                  let curNode = myDiagram.findNodeForKey(nodedata.key);
                  options.contextMenu(curNode, cmd.text);
                  // console.log("On " + nodedata.text + "  " + cmd.text + ": " + cmd.action);
                }
              }
            )
          })
 }
        {
          text: "開始策略",
          figure: "Ellipse",
          fill: "#FEF7E7",
          stroke: '#FDCF90',
          info: "",
          type: "start",
          commands: [{ text: "查看", action: "view" }, { text: "刪除", action: "view" }],
        },

2.虛線及節點虛線框

{fromPort: "B", toPort: "T", from: -1, to: -3,category: "auditedDottedLine"}
// 自定義虛線樣式
  myDiagram.linkTemplateMap.add(
    "auditedDottedLine",
    $(
      go.Link,
      {
        selectable: true,
        selectionAdornmentTemplate: linkSelectionAdornmentTemplate
      },
      {
        relinkableFrom: true,
        relinkableTo: true,
        reshapable: true
      },
      {
        routing: go.Link.AvoidsNodes,
        curve: go.Link.JumpOver,
        corner: 5,
        toShortLength: 4
      },
      $(go.Shape, {
        isPanelMain: true,
        strokeWidth: 2,
        strokeDashArray: [3, 3]
      }),
      $(go.Shape, {
        toArrow: "Standard",
        stroke: null
      }),
      $(
        go.Panel,
        "Auto",
        $(
          go.Shape,
          "RoundedRectangle",
          new go.Binding("fill", "text", function(v) {
            return v ? "#F8F8F8" : null;
          }),
          {
            stroke: null,
            fill: null
          }
        ),
        $(
          go.TextBlock,
          {
            segmentIndex: 1,
            segmentFraction: 0.5,
            textAlign: "center",
            font: "10pt helvetica, arial, sans-serif",
            stroke: "blue",
            margin: 2,
            minSize: new go.Size(10, NaN)
            // editable: true
          },
          new go.Binding("text").makeTwoWay()
        )
      )
    )
  );
設置節點虛線邊框
    $(go.Shape, "Rectangle", { width: 40, height: 60, margin: 4, fill: null, strokeWidth: 2, strokeDashArray: [6, 6, 2, 2] }),
  myDiagram.nodeTemplate = $(
    go.Node,
    "Spot", $( go.Panel, "Auto", { name: "PANEL" }, new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify), $( go.Shape, "Rectangle", { portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer", fill: "white", stroke: "black", strokeWidth: 1 }, new go.Binding("figure"), new go.Binding("strokeDashArray"), new go.Binding("fill") ), )

 

3.自動補全

selectionChanged: function(eventPart) {
var dom = $$(".my-diagram-div canvas");
        dom.unbind("click", myFunction).bind("click", myFunction);
        function myFunction(e) {
          let position = {
            x: e.clientX,
            y: e.clientY
          };
          options.changeNodeSelection(eventPart.data, position);
          dom.unbind("click", myFunction);
        }
      }

 

let canvas = $(".my-diagram-div");
let selectHandlerDom = $('.select-handler');
            canvas.on('input propertychange','textarea',e => {
              selectHandlerDom.css({
                top:position.y + 20 +'px',
                left:position.x - 20 +'px',
                'z-index':10000
              });
              this.filterHandlerOptions(e.target.value);
            })
            selectHandlerDom.unbind('click').on('click','li',e => {
              let val = $(e.target).text()
              this.myDiagram.model.setDataProperty(data, 'text',val);
              canvas.find('textarea').val(val);
              
            })
            canvas.click(e => {
              selectHandlerDom.css({
                'z-index':-1
              })
            })

4.goJS版本過低操作節點時引起的事務執行出錯信息

http://zhonganphp-1251460743.cossh.myqcloud.com/a00000/common_js/gojs.js

5.去除水印

 

 

  1) 在文件中搜索7eba17a4ca3b1a8346,找到類似a.Jv=d[w.Jg("7eba17a4ca3b1a8346")][w.Jg("78a118b7")](d,w.um,4,4);這樣結構的代碼

  2) 將其注釋,替換成a.Jv=function(){return true;};

6.設置了布局引起連線不美觀的問題

layout: $(go.TreeLayout, { angle: 90 }),

設置了布局,連線的起點和重點就會受布局的影響,可以即使不保存節點的位置信息,繪制出來的圖形也按照一定規律排列


layout: $(go.LayeredDigraphLayout, { isInitial: false, direction: 90, columnSpacing: 50, isOngoing: false, layerSpacing: 50 }),

取消了布局設置,需要保存節點的位置信息,才能還原出節點的連線位置信息

 
 

 7. 圖形對象part和data屬性,有些事件傳入的事件對象,不能直接獲取data屬性,要先索引part屬性,然后再去引用data屬性

      $(
        go.TextBlock,
        {
          font: "bold 11pt Helvetica, Arial, sans-serif",
          margin: 8,
          maxSize: new go.Size(300, NaN),
          wrap: go.TextBlock.WrapFit,
          editable: true,
          textEdited: function(textBlock, previousText, currentText) {
            console.log(textBlock,textBlock.part.data);
            options.changeNodeSelection(textBlock.part.data);
          }
        },

        new go.Binding("text").makeTwoWay()
      )

 

 8.下載canvas圖片

makeImageData方法通過HTMLCanvasElement.toDataURL()方法實現的

    downLoadImage(name) {
      // 不設置,下載的圖片殘缺不全
      // this.myDiagram.autoScale = go.Diagram.Uniform;
      var a = document.createElement("a");
      a.href = this.myDiagram.makeImageData({
        scale: 1,
        type: "image/png",
        maxSize: new go.Size(Infinity, Infinity)
      });
      a.download = name;
      a.click();
    },

9. goJS圖表實例重復初始化報錯

Invalid div id; div already has a Diagram associated with it.

解決方法:myDiagram.div=null;

 10.設置了樹形折疊菜單之后,當樹形菜單有超過一級以上的葉子節點時,本該隱藏的葉子節點顯示出來,解決的要點,要向下面這樣設置顯示和隱藏

      myDiagram.nodeTemplate =
        $(go.Node, "Horizontal",
          {
            deletable: false,
            selectable: false,
            isTreeExpanded: false,//折疊全部子節點
            // toolTip: tooltiptemplate,
          },

      myDiagram.addDiagramListener("InitialLayoutCompleted", function (e) {
        if (EditType != 'export') {
          // 只展示一級菜單
          e.diagram.findTreeRoots().each(function (r) { r.expandTree(2) });
        }
      });

 

 

一些小功能點的實現

添加圖例

 

      myDiagram.add(
        $(go.Part, "Table",
          { position: new go.Point(300, 10), selectable: false },
          $(go.TextBlock, "Key",
            { row: 0, font: "700 14px Droid Serif, sans-serif" }),  // end row 0
          $(go.Panel, "Horizontal",
            { row: 1, alignment: go.Spot.Left },
            $(go.Shape, "Rectangle",
              { desiredSize: new go.Size(30, 30), fill: bluegrad, margin: 5 }),
            $(go.TextBlock, "Males",
              { font: "700 13px Droid Serif, sans-serif" })
          ),  // end row 1
          $(go.Panel, "Horizontal",
            { row: 2, alignment: go.Spot.Left },
            $(go.Shape, "Rectangle",
              { desiredSize: new go.Size(30, 30), fill: pinkgrad, margin: 5 }),
            $(go.TextBlock, "Females",
              { font: "700 13px Droid Serif, sans-serif" })
          )  // end row 2
        ));
 上下兩行文字

 myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          { deletable: false, toolTip: tooltiptemplate },
          new go.Binding("text", "name"),
          $(go.Shape, "Rectangle",
            {
              fill: "orange",
              stroke: "black",
              stretch: go.GraphObject.Fill,
              alignment: go.Spot.Center
            },
            new go.Binding("fill", "gender", genderBrushConverter)),
          $(go.Panel, "Vertical",
            $(go.TextBlock,
              {
                font: "bold 8pt Helvetica, bold Arial, sans-serif",
                alignment: go.Spot.Center,
                margin: 6
              },
              new go.Binding("text", "name")),
            $(go.TextBlock,
              new go.Binding("text", "kanjiName"))
          )
        );
展開折疊按鈕效果實現 faultTree.html

 

myDiagram.nodeTemplate = $(
          ...
          $("TreeExpanderButton", { alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, "ButtonBorder.figure": "Rectangle" })
...
框內折疊  entityRelationship.html

      // define the Node template, representing an entity
      myDiagram.nodeTemplate =
        $(go.Node, "Auto",  // the whole node panel
          {
            selectionAdorned: true,
            resizable: true,
            layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,
            fromSpot: go.Spot.AllSides,
            toSpot: go.Spot.AllSides,
            isShadowed: true,
            shadowColor: "#C5C1AA"
          },
          new go.Binding("location", "location").makeTwoWay(),
          // whenever the PanelExpanderButton changes the visible property of the "LIST" panel,
          // clear out any desiredSize set by the ResizingTool.
          new go.Binding("desiredSize", "visible", function(v) { return new go.Size(NaN, NaN); }).ofObject("LIST"),
          // define the node's outer shape, which will surround the Table
          $(go.Shape, "Rectangle",
            { fill: lightgrad, stroke: "#756875", strokeWidth: 3 }),
          $(go.Panel, "Table",
            { margin: 8, stretch: go.GraphObject.Fill },
            $(go.RowColumnDefinition, { row: 0, sizing: go.RowColumnDefinition.None }),
            // the table header
            $(go.TextBlock,
              {
                row: 0, alignment: go.Spot.Center,
                margin: new go.Margin(0, 14, 0, 2),  // leave room for Button
                font: "bold 16px sans-serif"
              },
              new go.Binding("text", "key")),
            // the collapse/expand button
            $("PanelExpanderButton", "LIST",  // the name of the element whose visibility this button toggles
              { row: 0, alignment: go.Spot.TopRight }),
            // the list of Panels, each showing an attribute
            $(go.Panel, "Vertical",
              {
                name: "LIST",
                row: 1,
                padding: 3,
                alignment: go.Spot.TopLeft,
                defaultAlignment: go.Spot.Left,
                stretch: go.GraphObject.Horizontal,
                itemTemplate: itemTempl
              },
              new go.Binding("itemArray", "items"))
          )  // end Table Panel
        );  // end Node
拖拽回收站功能實現 動態創建節點和連線 flowBuilder.html 

      myDiagram.nodeTemplateMap.add("Recycle",
        $(go.Node, "Auto",
          {
            portId: "to", toLinkable: true, deletable: false,
            layerName: "Background", locationSpot: go.Spot.Center
          },
          new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
          { dragComputation: function(node, pt, gridpt) { return pt; } },
          { mouseDrop: function(e, obj) { myDiagram.commandHandler.deleteSelection(); } },
          $(go.Shape,
            { fill: "lightgray", stroke: "gray" }),
          $(go.TextBlock, "Drop Here\nTo Delete",
            { margin: 5, textAlign: "center" })
        ));
 提示工具

function makeTooltip(str) {  
// a helper function for defining tooltips for buttons
        return $("ToolTip",
          $(go.TextBlock, str));
}
創建組

    myDiagram.groupTemplate =
        $(go.Group, "Auto",
          { // define the group's internal layout
            layout: $(go.TreeLayout,
              { angle: 90, arrangement: go.TreeLayout.ArrangementHorizontal, isRealtime: false }),
            // the group begins unexpanded;
            // upon expansion, a Diagram Listener will generate contents for the group
            isSubGraphExpanded: false,
            // when a group is expanded, if it contains no parts, generate a subGraph inside of it
            subGraphExpandedChanged: function(group) {
              if (group.memberParts.count === 0) {
                randomGroup(group.data.key);
              }
            }
          },
          $(go.Shape, "Rectangle",
            { fill: null, stroke: "gray", strokeWidth: 2 }),
          $(go.Panel, "Vertical",
            { defaultAlignment: go.Spot.Left, margin: 4 },
            $(go.Panel, "Horizontal",
              { defaultAlignment: go.Spot.Top },
              // the SubGraphExpanderButton is a panel that functions as a button to expand or collapse the subGraph
              $("SubGraphExpanderButton"),
              $(go.TextBlock,
                { font: "Bold 18px Sans-Serif", margin: 4 },
                new go.Binding("text", "key"))
            ),
            // 設置 go.Placeholder 對象的目的是, 讓組自適應內部節點的大小;
            $(go.Placeholder,
              { padding: new go.Margin(0, 10) })
          )  // end Vertical Panel
        );  // end Group
myDiagram.model.addNodeData({ key: name, isGroup: true, group: group });
交互面板

          var inspector = new Inspector("myInfo", myDiagram, {
            properties: {
              // key would be automatically added for nodes, but we want to declare it read-only also:
              "key": { readOnly: true, show: Inspector.showIfPresent },
              // fill and stroke would be automatically added for nodes, but we want to declare it a color also:
              "fill": { show: Inspector.showIfPresent, type: 'color' },
              "stroke": { show: Inspector.showIfPresent, type: 'color' }
            }
          });

 自定義tooltip

      // get tooltip text from the object's data
      function tooltipTextConverter(person) {
        var str = "";
        str += "Born: " + person.birthYear;
        if (person.deathYear !== undefined) str += "\nDied: " + person.deathYear;
        if (person.reign !== undefined) str += "\nReign: " + person.reign;
        return str;
      }

      // define tooltips for nodes
      var tooltiptemplate =
        $("ToolTip",
          { "Border.fill": "whitesmoke", "Border.stroke": "black" },
          $(go.TextBlock,
            {
              font: "bold 8pt Helvetica, bold Arial, sans-serif",
              wrap: go.TextBlock.WrapFit,
              margin: 5
            },
            new go.Binding("text", "", tooltipTextConverter))
        );

      myDiagram.nodeTemplate =
        $(go.Node, "Horizontal",
          {
            deletable: false,
            selectable: false,
            toolTip: tooltiptemplate,
          })

 

其它效果參見goJS的示例

1.各種箭頭樣式 arrowheads.html
2.所有的形狀shapes.html
3.自定義節點上下文 customContextMenu.html
4.自定義卷起折疊 customExpandCollapse.html 
5.自定義選擇輸入框 customTextEditingTool.html
6.定義多個連線的入口和出口 dataFlow.html draggablePorts.html
   鼠標右鍵動態增刪端口 dynamicPorts.html
7.實體關系 連線會動態鏈接 避免交錯在一起 entityRelationship.html
8.鼠標經過時顯示多行節點信息 dataVisualization.html
9.拖動排序 dragDropFields.html
10.限制節點在特定范圍內移動 且不能觸碰到邊緣 dragUnoccupied.html
11.手勢縮放功能 gestureBehavior.html
12.鼠標經過時顯示按鈕 hoverButtons.html

 

 常用的API

批量刪除連線
var removeLinks=[];
//首先拿到這個節點的對象
var node = myDiagram.findNodeForKey('key');
//獲取節點所有線
node.findLinksConnected().each(function(link) { 
     removeLinks.push(link.data);
    }
 );
myDiagram.model.removeLinkDataCollection(removeLinks);
監聽連線完成事件
myDiagram.addDiagramListener("LinkDrawn",function(e){
       (e.subject.data )    //這是這個線條的數據
 }) ;
監聽新拖拽到畫布的節點
diagram.addModelChangedListener(function(evt) {
    // ignore unimportant Transaction events
    if (!evt.isTransactionFinished) return;
    var txn = evt.object;  // a Transaction
    if (txn === null) return;
    // iterate over all of the actual ChangedEvents of the Transaction
    txn.changes.each(function(e) {
      // ignore any kind of change other than adding/removing a node
      if (e.modelChange !== "nodeDataArray") return;
      // record node insertions and removals
      if (e.change === go.ChangedEvent.Insert) {
        console.log(evt.propertyName + " added node with key: " + e.newValue.key);
      } else if (e.change === go.ChangedEvent.Remove) {
        console.log(evt.propertyName + " removed node with key: " + e.oldValue.key);
      }
    });
  });
通過key值去查找節點
myDiagram.findNodeForKey(key).data   //key值是節點的key
添加節點
let nodeData={ text: "Start", figure: "Ellipse", fill: "#00AD5F", info: "", type: "start" };
myDiagram.model.addNodeData(nodeData); // 須有位置信息
刪除節點
myDiagram.remove(part)
添加線
let linkData ={ from: newnode.data.key, to: oldnode.data.key, text: "true", side: "Left", isHighlighted:false};
myDiagram.model.addLinkData(linkData); // linkData是連線數據
 
        
查找該節點的下一級節點
key.findNodesOutOf() 
獲取當前畫布的json
myDiagram.model.toJson(); // 得到結果為json字符串
加載json刷新畫布
myDiagram.model = go.Model.fromJson(model); // 傳入參數model為json字符串
獲取節點對象
var node=myDiagram.findNodeForKey('key');
獲取節點data
var nodeData=myDiagram.model.findNodeDataForKey('key');
獲取畫布全部節點
var nodes=myDiagram.nodes;
//遍歷輸出節點對象
nodes.each(function (node) {
console.log(node.data.text);
});
根據甲,找甲的子級元素
node.findTreeChildrenNodes().each(function(cNode) {
console.log(cNode.data)
});
獲取節點的線
var node=myDiagram.findNodeForKey('key');

node.findLinksConnected().each(function(link) {console.log(link.data)});
獲取從節點出來的線
var node=myDiagram.findNodeForKey('key');

node.findLinksOutOf().each(function(link) {console.log(link.data)});

 

更新節點
// 修改單個節點屬性
myDiagram.model.updateTargetBindings(node.data) 

// 批量修改節點屬性
myDiagram.model.nodeDataArray
myDiagram.model.linkDataArray
//修改完成調用以下方法完成重建
myDiagram.rebuildParts()

 

更新節點API

// 更新方式一
let curDataNode = myDiagram.model.findNodeDataForKey(this.clickNodeKey); curDataNode.nodeItem = this[obj.type + "Info"]; myDiagram.model.updateTargetBindings(curDataNode);
// 更新方式二 myDiagram.model.setDataProperty(curNode,
"data", curNode.data); console.log("curNode.data", curNode.data);

 

goJS資料

2.https://www.cnblogs.com/helloluckworld/articles/9592238.html 去除水印(實用)

3.goJS 繪制web流程圖  https://blog.csdn.net/kenhins/article/details/79043198 (收獲很大)

4.關於 Gojs 你可能用到的方法 / gojs自定義 / gojs

5.https://blog.csdn.net/sinat_20522337/article/details/79158122

6.https://juejin.im/post/5b875d85e51d4538c0220603

7.https://gojs.net/latest/intro/nodes.html  官方教程

8.https://blog.csdn.net/qq_29287561/article/details/81066004 畫布比例自適應

9. goJS入門教程    https://liuxiaofan.com/2018/03/16/3521.html

10. gojS事件  https://blog.csdn.net/pdw2009/article/details/82993971

11. gojs 初探  https://www.wengbi.com/thread_50581_1.html

12. gojs的一些使用技巧 https://blog.csdn.net/MEdwardM/article/details/52528236

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM