使用 sigma.js 展开和折叠

Expand and collapse using sigma.js

目标

我正在尝试在 sigma.js 中实现展开和折叠。右键单击任何节点,它会添加新节点并连接其边缘,但它会放置在随机位置。

我想在空闲 space 中添加节点,它们不应与其他节点碰撞或重叠。它应该随着动画慢慢展开,在空闲 space 区域展开,如 this example. Related.

代码

<!DOCTYPE html>
<html>
<head>
    <title> Airlines Graph Render </title>
    <script src="../build/sigma.min.js"></script>
    <script src="../src/renderers/canvas/sigma.canvas.edges.curvedArrow.js"></script>

    <script src="../plugins/sigma.layout.forceAtlas2/worker.js"></script>
    <script src="../plugins/sigma.layout.forceAtlas2/supervisor.js"></script>
    <script src="../plugins/sigma.renderers.edgeLabels/settings.js"></script>
    <script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.curve.js"></script>
    <script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.def.js"></script>
    <script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.curvedArrow.js"></script>
    <style>
        body,html{
            width: 100%;
            margin: 0px;
            padding: 0px;
            height: 100%
        }
        #graph-container {
          width:100%;
          height: 100%;
        }
  </style>
</head>
<body>

      <div id="graph-container"></div>

    <script >
        
        var graph = {
                "nodes": [
                    {
                        "city": "Dallas",
                        "area": 999,
                        "code": 214,
                        "country": "USA"
                    },
                    {
                        "city": "Austin",
                        "area": 1180,
                        "code": 512,
                        "country": "USA"
                    },
                    {
                        "city": "New York",
                        "area": 1214,
                        "code": 646,
                        "country": "USA"
                    },
                    {
                        "city": "Washington",
                        "area": 176,
                        "code": 564,
                        "country": "USA"
                    },
                    {
                        "city": "Atlanta",
                        "area": 342,
                        "code": 518,
                        "country": "USA"
                    },
                    {
                        "city": "Huston",
                        "area": 1625,
                        "code": 281,
                        "country": "USA"
                    },
                    {
                        "city": "Chicago",
                        "area": 606,
                        "code": 312,
                        "country": "USA"
                    },
                    {
                        "city": "London",
                        "area": 909,
                        "code": 312,
                        "country": "England"
                    }
                ],
                "edges": [
                    {
                        "key": 1,
                        "source": "Dallas",
                        "destination": "Austin",
                        "distance": 200,
                        "airlines": "British Airways",
                        "fare": 220
                    },
                    {
                        "key": 2,
                        "source": "Austin",
                        "destination": "Dallas",
                        "distance": 200,
                        "airlines": "Lufthansa",
                        "fare": 120
                    },
                    {
                        "key": 3,
                        "source": "Washington",
                        "destination": "Dallas",
                        "distance": 1300,
                        "airlines": "Lufthansa",
                        "fare": 300
                    },
                    {
                        "key": 4,
                        "source": "Atlanta",
                        "destination": "Washington",
                        "distance": 600,
                        "airlines": "Lufthansa",
                        "fare": 600
                    },
                    {
                        "key": 5,
                        "source": "Washington",
                        "destination": "Atlanta",
                        "distance": 600,
                        "airlines": "KLM",
                        "fare": 400
                    },
                    {
                        "key": 6,
                        "source": "New York",
                        "destination": "Atlanta",
                        "distance": 300,
                        "airlines": "Qatar",
                        "fare": 1300
                    },
                    {
                        "key": 7,
                        "source": "Huston",
                        "destination": "Atlanta",
                        "distance": 800,
                        "airlines": "Indigo",
                        "fare": 400
                    },
                    {
                        "key": 8,
                        "source": "Atlanta",
                        "destination": "Huston",
                        "distance": 800,
                        "airlines": "Spicejet",
                        "fare": 600
                    },
                    {
                        "key": 9,
                        "source": "New York",
                        "destination": "Chicago",
                        "distance": 1000,
                        "airlines": "Air China",
                        "fare": 500
                    },
                    {
                        "key": 10,
                        "source": "Chicago",
                        "destination": "New York",
                        "distance": 1000,
                        "airlines": "Jet Airways",
                        "fare": 200
                    },
                    {
                        "key": 11,
                        "source": "Dallas",
                        "destination": "Chicago",
                        "distance": 900,
                        "airlines": "Lufthansa",
                        "fare": 1300
                    },
                    {
                        "key": 12,
                        "source": "Austin",
                        "destination": "Huston",
                        "distance": 160,
                        "airlines": "Lufthansa",
                        "fare": 240
                    },
                    {
                        "key": 13,
                        "source": "Dallas",
                        "destination": "New York",
                        "distance": 780,
                        "airlines": "Lufthansa",
                        "fare": 300
                    }
                ]
            };
        var g = {
            nodes:[],
            edges:[]
        }

    // Generate a random graph:
        
            colors = [
              '#617db4',
              '#668f3c',
              '#c6583e',
              '#b956af'
            ];
         sigma.utils.pkg('sigma.canvas.nodes');
         sigma.canvas.nodes.border = function(node, context, settings) {
              var prefix = settings('prefix') || '';

              context.beginPath();
              context.arc(
                node[prefix + 'x']+15,
                node[prefix + 'y'],
                node[prefix + 'size']-2,
                0,
                Math.PI * 2,
                true
              );
              //context.fillStyle = "orange";
              context.strokeStyle = node.color || settings('defaultNodeColor');
              //get the data from the group
              //var data = d3.select(this).data();
              context.stroke();
              //context.fill();
              context.font = "10px Arial";
              context.fillStyle = "black";
              context.strokeStyle = "black";
              //write the text in the context
              context.fillText(10,node[prefix + 'x']+15+ 10,  node[prefix + 'size']-2-15);

              // Adding a border
              //context.lineWidth = node.borderWidth || 1;
              //context.strokeStyle = node.borderColor || '#fff';
              //context.stroke();

              context.fillStyle = node.color || settings('defaultNodeColor');
              context.beginPath();
              context.arc(
                node[prefix + 'x'],
                node[prefix + 'y'],
                node[prefix + 'size'],
                0,
                Math.PI * 2,
                true
              );

             context.closePath();
             context.fill();

            };

        for (var i = 0; i < graph.nodes.length; i++)
          g.nodes.push({
            id: graph.nodes[i]['city'],
            label: graph.nodes[i]['city'],
            x: Math.random(),
            y: Math.random(),
            size: 8,
            color: colors[Math.floor(Math.random() * colors.length)]
          });

        for (var i = 0; i < graph.edges.length; i++)
          g.edges.push({
            id: graph.edges[i]['key'],
            source: graph.edges[i]['source'],
            target: graph.edges[i]['destination'],
            size: 8,
            label:graph.edges[i]['airlines'],
            color: '#668e3e',
            type:'curvedArrow'
          });

        s = new sigma({
          graph: g,
          renderer: {
            container: document.getElementById('graph-container'),
            type: 'canvas'
          },
          settings: {
            edgeLabelSize: 'proportional',
            minNodeSize: 1,
            maxNodeSize: 10,
            minEdgeSize: 0.1,
            maxEdgeSize: 2,
            enableEdgeHovering: true,
            edgeHoverSizeRatio: 2,
            defaultNodeType: 'border',
            defaultNodeColor:"#fff",
            mouseEnabled: true,
            touchEnabled: true
          }
        });

        //s.settings('autoRescale', false)

        s.startForceAtlas2({worker: true, barnesHutOptimize: false});
        s.stopForceAtlas2();

        s.bind('rightClickNode', function(e) {
                
              console.log(e.type, e.data.node.label, e.data.captor);
                var name = 'New City'+Math.random();
                s.graph.addNode({
                id: name,
                label: 'baai',
                x: Math.random(),
                y: Math.random(),
                size: 8,
                color: colors[Math.floor(Math.random() * colors.length)]
              });
              s.graph.addEdge({
                id: name +Math.random(),
                source: e.data.node.id,
                target: name,
                size: 8,
                label:'bit'+Math.random(),
                color: '#668e3e',
                type:'curvedArrow'
              });
              
              // Edge with Already existing one
              s.graph.addEdge({
                id: name+Math.random(),
                source: 'Huston',
                target: name,
                size: 8,
                label:'New City'+Math.random(),
                color: '#668e3e',
                type:'curvedArrow'
              });           
            setTimeout(function(){
                s.refresh();
            },1000) 
            
        });
        
    </script>

</body>
</html>

尝试次数

单击节点时,我通过不断增加半径以圆形方式在其周围放置节点。在 JSFiddle 你可以看到这个。第一次点击很顺利,但在下一次点击时,它会在另一个圆圈中出现一个圆圈。我应该放多少半径才能让它看起来不像那样(如屏幕截图所示)?

第二次点击它相对定位,变成这个截图。但我想要实际定位而不是相对定位。

不使用位置 Math.random,而是将位置设置为靠近被单击的节点,并让力布局负责将其移到正确的位置。所以,你应该改变这个:

s.graph.addNode({
    x: Math.random(),
    y: Math.random(),

到那个:

s.graph.addNode({
    x: clickedNode.x + 2* Math.random() - 1,
    y: clickedNode.y + 2* Math.random() - 1,

Fruchterman Reingold 力导向图布局 (algorithm summary) 将节点之间的力表示为弹簧连接钢响铃,并不断尝试在所有节点之间找到平衡。
它是 available as a plugin in Linkurious(Sigma.js 的分支)。这种布局可能正是您所需要的。

使用您自己的代码,并且以下依赖项:
sigma.plugins.animate sigma.layouts.fruchtermanReingold(来自 Linkurious 分支)

我得到了以下图形可视化:

为此,在您启动 Force Atlas 2 布局的地方,将其替换为以下内容:

sigma.layouts.fruchtermanReingold.configure(sigmaInstance, {easing: 'quadraticOut'});
sigma.layouts.fruchtermanReingold.start(sigmaInstance);

而且,最重要的部分是,在生成新节点或删除节点后,您必须重新运行 布局:

sigmaInstance.refresh();
sigma.layouts.fruchtermanReingold.start(sigmaInstance);

现在,一些提示

  • 没有必要 setTimeout() 让它工作。请记住在每次图形更新后重新运行 力布局。
  • 您的代码似乎重现了比您的屏幕截图显示的更丰富的可视化效果 - 具有弯曲的边缘、标签等。确保所有依赖项都已实际加载。
  • 如果你确实使用弯曲的边缘,力布局仍然可以正常工作,但表示可能有点奇怪,所以考虑不使用它。
  • Fruchterman-Reingold 布局可以配置自定义持续时间、重力、缓动方法等。阅读其文档以发挥其全部功能。