gojs practical advanced usage

Everybody, happy New Year!

Historical articles:

This article describes the use of gojs Some solutions to the problems you may encounter in the process of drawing.

gojs is a very powerful js library for visualizing relationships.

1. Cancel updating animation

Problem: when updating data, rendering will be triggered, rendering animation is available, and the user experience is poor.

Scheme: initial data drawing with animation; Update data drawing, no animation.

Code implementation:

// The diagram s used later are instances created by gojs
// diagram_container is the domain ID of the graph container
diagram = $(go.Diagram, 'diagram_container') 

Scheme I:

function updateData (nodeArr = [], linkArr = [], hasAnimation = true ) {
  if (hasAnimation) {
    diagram.model = new go.GraphLinksModel(nodeArr, linkArr);
  } else {
    diagram.model.nodeDataArray = nodeArr
    diagram.model.linkDataArray = linkArr
  }
}

// After initializing an instance, it is processed only once
diagram.animationManager.canStart = function(reason) {
  if (reason === 'Model') return false
  return true
}

Scheme II:

// Bind the data to the diagram and draw the diagram
function updateData (nodeArr = [], linkArr = [], hasAnimation = true ) {
  if (hasAnimation) {
    diagram.model = new go.GraphLinksModel(nodeArr, linkArr);
  } else {
    diagram.model.nodeDataArray = nodeArr
    diagram.model.linkDataArray = linkArr
    diagram.animationManager.stopAnimation()
  }
}

Scheme III:

// Bind the data to the diagram and draw the diagram
function updateData (nodeArr = [], linkArr = [], hasAnimation = true) {
  diagram.model = new go.GraphLinksModel(nodeArr, linkArr);
  if (diagram.animationManager) {
    // Default has animation, None has no animation
    diagram.animationManager.initialAnimationStyle = hasAnimation ? go.AnimationManager.Default : go.AnimationManager.None;
  }
}

2. Export diagram (including the part outside the visual area)

Problem: the exported image, which is realized by using the relevant api of the native canvas, only contains the images in the visual area

Solution: use the api provided by gojs to process

Principle behind it: use the data to redraw a graph, all data nodes are in the visible area of the graph, and then use the native canvas related api to export the picture

Code implementation:

function downloadImg = ({
  imgName = 'dag',
  bgColor = 'white',
  imgType = 'image/png'
}= {}) {
  diagram.makeImageData({
    scale: 2,
    padding: new go.Margin(50, 70),
    maxSize: new go.Size(Infinity, Infinity),
    background: bgColor,
    type: imgType,
    returnType: 'blob',
    callback: (blob: any) => {
      const url = window.URL.createObjectURL(blob)
      const fileName = imgName + '.png'
      const aEl = document.createElement('a')
      aEl.style.display = 'none'
      aEl.href = url
      aEl.download = fileName

      // IE 11
      if (window.navigator.msSaveBlob !== undefined) {
        window.navigator.msSaveBlob(blob, fileName)
        return
      }

      document.body.appendChild(aEl)
      requestAnimationFrame(function() {
        aEl.click()
        window.URL.revokeObjectURL(url)
        document.body.removeChild(aEl)
      })
    }
  })
}

3. Disable ctrl related shortcut keys

// Disable ctl related operations
diagram.commandHandler.doKeyDown = function() {
  const e = diagram.lastInput
  const control = e.control || e.meta
  const key = e.key

  // Cancel Ctrl + A / Z / Y / g a-select all, z-undo, y-redo, G-group
  if (control && ['A', 'Z', 'Y', 'G'].includes(key)) return
  // Cancel Del/Backspace delete key
  if (key === 'Del' || key === 'Backspace') return

  go.CommandHandler.prototype.doKeyDown.call(this)
}

4. Canvas scrolling mode, unlimited scrolling or local scrolling

Problem: the touch key on the mac can slide left and right to control the forward and backward of the browser page, which is easy to trigger

Scheme: enable unlimited scrolling to avoid the user accidentally triggering the forward and backward of the browser

Code implementation:

function infiniteScroll = (infiniteScroll) {
  this.diagram.scrollMode = infiniteScroll ? go.Diagram.InfiniteScroll : go.Diagram.DocumentScroll
}

5. Expand and collapse multi-level nested groups

Problem: groups are nested in multiple layers. After all the groups are expanded, click a single group to collapse. The first click is invalid, and the second click is effective

Code implementation:

Method 1: nodeArr does not bind the expand / collapse attribute

// groupIds is the ids of all group s, from outside to inside. The assembly data is collected at the beginning of traversal
// groupIdsReverse is the ids of all group s, from inside to outside
// Expand all, from outside to inside
// Put it all away, from inside to outside
function setExpandCollapse (isExpand, groupIds, groupIdsReverse) {
  // Unfolding and folding need to be handled from two directions. Only when unfolding and folding again can the interaction be normal. Otherwise, the first point is invalid and the second point is limited
  let arr = isExpand ? groupIds : groupIdsReverse;
  let group;

  arr.forEach(id => {
    group = diagram.findNodeForKey(id);
    group.isSubGraphExpanded = isExpand;
  })
},

Method 2: nodeArr binding expands and retracts the attribute isExpanded

function setExpandCollapse (isExpand) {
  const { nodeDataArray, linkDataArray } = diagram.model
  const newNodeArr = nodeDataArray.map(v => {
    if (v.isGroup) {
      return {...v, isExpanded: isExpand}
    }
    return v
  })

  // The above method
  updateData(newNodeArr, linkArr, false)
}

6. Animate drawing elements

  • Dashed animation
  • icon loading rotation animation

Code implementation:

function loop = () {
  const animationTimer = setTimeout(() => {
    clearTimeout(animationTimer)
    const oldskips = diagram.skipsUndoManager;
    diagram.skipsUndoManager = true;

    // Dashed animation
    diagram.links.each((link: any) => {
      const dashedLinkShape = link.findObject("dashedLink");
      if (dashedLinkShape) {
        const off = dashedLinkShape.strokeDashOffset - 3;
        // Animate (move) strokes
        dashedLinkShape.strokeDashOffset = (off <= 0) ? 60 : off;
      }
    });

    // loading rotation
    diagram.nodes.each((node: any) => {
      const loadingShape = node.findObject("loading");
      if (loadingShape) {
        const angle = loadingShape.angle + 20;
        // Animate (move) strokes
        loadingShape.angle = (angle == 0) ? 360 : angle;
      }
    });

    diagram.skipsUndoManager = oldskips;
    loop();
  }, 180);
}
loop()

7. Modify the selected style

Problem: box selection style: the default is red, which does not match the custom drawing color

diagram.toolManager.dragSelectingTool.box = $(go.Part,
  { layerName: "Tool", selectable: false },
  $(go.Shape,
    { name: "SHAPE", fill: 'rgba(104, 129, 255, 0.2)', stroke: 'rgba(104, 129, 255, 0.5)', strokeWidth: 2 }));

I hope it will help you. If it helps, please save it. Thank you!

Keywords: Visualization

Added by susannah on Wed, 05 Jan 2022 10:42:15 +0200