JointJS在元素之间创建多个链接

时间:2016-12-22 18:26:27

标签: javascript jointjs

我正在尝试使用JointJS创建图表like this

然而,当我在元素之间添加多个链接时,我只看到1个链接出现。如何添加多个链接,并在它们之间自动调整空间?

这是添加框和链接的代码。请注意,现在我只想在所有块之间添加3个链接,但我只看到每个块之间出现1个链接。

var steps = [{title: "Step 1"}, {title: "Step 2"}, {title: "Step 3"}];

steps.forEach(function(step, i){
    var title = step.title;
    var yOffset = i*150 + 50; //offsets first block by 50 in y and all others 150
    var xOffset = 60; //offsets all blocks by 60
    createBlock(title, xOffset, yOffset, i);
});

var blocks = [];

function createBlock(title, x, y, loc) {
    var x = (typeof x !== 'undefined') ?  x : 0;
    var y = (typeof y !== 'undefined') ?  y : 0;

    var newBlock = new joint.shapes.html.Element({
        position: { x: x, y: y },
        size: { width: 170, height: 100 },
        label: title,
        attrs: {
            '.label': {
                text: title,
                'ref-x': .5, 
                'ref-y': .4,
                fill: '#FFFFFF'
            },
        }
    });

    blocks.push(newBlock.id);

    graph.addCell(newBlock);

    if(blocks.length > 1) {
        var link = new joint.shapes.devs.Link({
            source: {
                id: blocks[loc-1],
            },
            target: {
                id: blocks[loc],
            },
        });
        graph.addCell(link);

        var link2 = new joint.shapes.devs.Link({
            source: {
                id: blocks[loc-1],
            },
            target: {
                id: blocks[loc],
            },
        });
        graph.addCell(link2);

        var link3 = new joint.shapes.devs.Link({
            source: {
                id: blocks[loc-1],
            },
            target: {
                id: blocks[loc],
            },
        });
        graph.addCell(link3);
    }
}

2 个答案:

答案 0 :(得分:0)

所有链接都位于彼此之上,因此您将其视为单个链接。在jointjs的演示中有代码可以查看不同路径中的每个链接。您可以添加以下代码,并看到链接显示在不同的路径中。您需要在以下三行中将图表更改为图表名称

// displaying multiple links between two elements in different paths
      function adjustVertices(graph, cell) {
          // If the cell is a view, find its model.
          cell = cell.model || cell;
          if (cell instanceof joint.dia.Element) {
              _.chain(graph.getConnectedLinks(cell)).groupBy(function(link) {
                  // the key of the group is the model id of the link's source or target, but not our cell id.
                  return _.omit([link.get('source').id, link.get('target').id], cell.id)[0];
              }).each(function(group, key) {
                  // If the member of the group has both source and target model adjust vertices.
                  if (key !== 'undefined') adjustVertices(graph, _.first(group));
              });
              return;
          }

          // The cell is a link. Let's find its source and target models.
          var srcId = cell.get('source').id || cell.previous('source').id;
          var trgId = cell.get('target').id || cell.previous('target').id;

          // If one of the ends is not a model, the link has no siblings.
          if (!srcId || !trgId) return;

          var siblings = _.filter(graph.getLinks(), function(sibling) {

              var _srcId = sibling.get('source').id;
              var _trgId = sibling.get('target').id;

              return (_srcId === srcId && _trgId === trgId) || (_srcId === trgId && _trgId === srcId);
          });

          switch (siblings.length) {

          case 0:
              // The link was removed and had no siblings.
              break;

          case 1:
              // There is only one link between the source and target. No vertices needed.
              cell.unset('vertices');
              break;

          default:

              // There is more than one siblings. We need to create vertices.
              // First of all we'll find the middle point of the link.
              var srcCenter = graph.getCell(srcId).getBBox().center();
              var trgCenter = graph.getCell(trgId).getBBox().center();
              var midPoint = joint.g.line(srcCenter, trgCenter).midpoint();

              // Then find the angle it forms.
              var theta = srcCenter.theta(trgCenter);

              // This is the maximum distance between links
              var gap = 20;

              _.each(siblings, function(sibling, index) {
                  // We want the offset values to be calculated as follows 0, 20, 20, 40, 40, 60, 60 ..
                  var offset = gap * Math.ceil(index / 2);
                  // Now we need the vertices to be placed at points which are 'offset' pixels distant
                  // from the first link and forms a perpendicular angle to it. And as index goes up
                  // alternate left and right.
                  //
                  //  ^  odd indexes
                  //  |
                  //  |---->  index 0 line (straight line between a source center and a target center.
                  //  |
                  //  v  even indexes
                  var sign = index % 2 ? 1 : -1;
                  var angle = joint.g.toRad(theta + sign * 90);
                  // We found the vertex.
                  var vertex = joint.g.point.fromPolar(offset, angle, midPoint);
                  sibling.set('vertices', [{ x: vertex.x, y: vertex.y }]);
              });
          }
      };


      var myAdjustVertices = _.partial(adjustVertices, graph);
      // adjust vertices when a cell is removed or its source/target was changed
      graph.on('add remove change:source change:target', myAdjustVertices);
      // also when an user stops interacting with an element.
      graph.on('cell:pointerup', myAdjustVertices);

答案 1 :(得分:0)

解决方案的核心在于下面介绍的AdjustVertices函数。它接受图和单元格(链接或元素)。为了增加便利,该函数接受单元格视图和模型。

如果单元格是链接,它将找到具有相同源和目标的所有链接,然后在它们上设置顶点;我们将这些相关链接称为“兄弟姐妹”。 如果cell是一个元素,我们将为连接到该元素的每个不同的链接(源和目标不同)执行功能。

function adjustVertices(graph, cell) {

    // if `cell` is a view, find its model
    cell = cell.model || cell;

    if (cell instanceof joint.dia.Element) {
        // `cell` is an element

        _.chain(graph.getConnectedLinks(cell))
            .groupBy(function(link) {

                // the key of the group is the model id of the link's source or target
                // cell id is omitted
                return _.omit([link.source().id, link.target().id], cell.id)[0];
            })
            .each(function(group, key) {

                // if the member of the group has both source and target model
                // then adjust vertices
                if (key !== 'undefined') adjustVertices(graph, _.first(group));
            })
            .value();

        return;
    }

    // `cell` is a link
    // get its source and target model IDs
    var sourceId = cell.get('source').id || cell.previous('source').id;
    var targetId = cell.get('target').id || cell.previous('target').id;

    // if one of the ends is not a model
    // (if the link is pinned to paper at a point)
    // the link is interpreted as having no siblings
    if (!sourceId || !targetId) return;

    // identify link siblings
    var siblings = _.filter(graph.getLinks(), function(sibling) {

        var siblingSourceId = sibling.source().id;
        var siblingTargetId = sibling.target().id;

        // if source and target are the same
        // or if source and target are reversed
        return ((siblingSourceId === sourceId) && (siblingTargetId === targetId))
            || ((siblingSourceId === targetId) && (siblingTargetId === sourceId));
    });

    var numSiblings = siblings.length;
    switch (numSiblings) {

        case 0: {
            // the link has no siblings
            break;

        } case 1: {
            // there is only one link
            // no vertices needed
            cell.unset('vertices');
            break;

        } default: {
            // there are multiple siblings
            // we need to create vertices

            // find the middle point of the link
            var sourceCenter = graph.getCell(sourceId).getBBox().center();
            var targetCenter = graph.getCell(targetId).getBBox().center();
            var midPoint = g.Line(sourceCenter, targetCenter).midpoint();

            // find the angle of the link
            var theta = sourceCenter.theta(targetCenter);

            // constant
            // the maximum distance between two sibling links
            var GAP = 20;

            _.each(siblings, function(sibling, index) {

                // we want offset values to be calculated as 0, 20, 20, 40, 40, 60, 60 ...
                var offset = GAP * Math.ceil(index / 2);

                // place the vertices at points which are `offset` pixels perpendicularly away
                // from the first link
                //
                // as index goes up, alternate left and right
                //
                //  ^  odd indices
                //  |
                //  |---->  index 0 sibling - centerline (between source and target centers)
                //  |
                //  v  even indices
                var sign = ((index % 2) ? 1 : -1);

                // to assure symmetry, if there is an even number of siblings
                // shift all vertices leftward perpendicularly away from the centerline
                if ((numSiblings % 2) === 0) {
                    offset -= ((GAP / 2) * sign);
                }

                // make reverse links count the same as non-reverse
                var reverse = ((theta < 180) ? 1 : -1);

                // we found the vertex
                var angle = g.toRad(theta + (sign * reverse * 90));
                var vertex = g.Point.fromPolar(offset, angle, midPoint);

                // replace vertices array with `vertex`
                sibling.vertices([vertex]);
            });
        }
    }
}

然后,我们附加必要的事件侦听器(功能bindInteractionEvents)。每当用户转换元素时,以及添加/删除链接或更改其源或目标时,都会重新计算顶点。

 function bindInteractionEvents(adjustVertices, graph, paper) {

        // bind `graph` to the `adjustVertices` function
        var adjustGraphVertices = _.partial(adjustVertices, graph);

        // adjust vertices when a cell is removed or its source/target was changed
        graph.on('add remove change:source change:target', adjustGraphVertices);

        // adjust vertices when the user stops interacting with an element
        paper.on('cell:pointerup', adjustGraphVertices);
    }