【问题标题】:D3: Multiple force layouts in one SVG?D3:一个 SVG 中的多个力布局?
【发布时间】:2013-04-25 12:18:43
【问题描述】:

我见过在单个页面上创建多个强制布局的解决方案,其中每个布局都包含在自己的 SVG 中;但是,我一直无法找到有关如何在单个 SVG 中包含多个力布局的帮助。每个布局都有自己的相关数据。

可以在http://jsfiddle.net/connorgr/SRAJa/ 找到我目前正在做的一个示例。我已经包含了下面代码的关键部分。最终结果看起来非常像除了最后一个节点/链接数据之外的所有内容的强制布局从未激活(或删除)。有什么办法可以防止这种情况发生吗?

由于我正在构建的可视化用例,我无法将数据合并在一起并仅使用一种布局。

/**
  * Creates a force layout in svgObj for each element in graphs
  * (svg) svgObj - The SVG to include the force layouts in
  * (json) graphs - An array of {"nodes":[...],"links":[...]} objects
  */
function generateMultiForce(svgObj, graphs) {
  for(var i=0; i < graphs.length; i++) {
    var graph = graphs[i];

    var graphArea = svgObj.append("g");

    var force = d3.layout.force()
        .charge(-200)
        .linkDistance(45)
        .size([width, height])
        .nodes(graph.nodes)
        .links(graph.links)
        .start();

    var link = graphArea.selectAll(".link")
        .data(graph.links)
        .enter().append("line")
          .attr("class", "link");

    var nodeGroup = graphArea.selectAll("g")
        .data(graph.nodes)
        .enter().append("g")
        .call(force.drag);

    var node = nodeGroup.append("circle")
        .attr("class", "node")
        .attr("r", 5)
        .style("fill", function(d) {
          return color(d.group); });

    var text = nodeGroup.append("text")
        .attr("x", 8)
        .attr("y", ".31em")
        .text(function(d) { return d.name; });

    force.on("tick", function() {
      link
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

      nodeGroup.attr("transform", function(d) {
          return "translate("+d.x+","+d.y+")";
        });
    });
  }
}

【问题讨论】:

  • 在我看来tick 事件(在某种意义上)是全球性的。也就是说,tick 事件只能有一个处理程序,因此您安装的最后一个会被调用。
  • 简要介绍了 D3 源代码,这似乎是正在发生的事情。无赖。

标签: layout d3.js force-layout


【解决方案1】:

以下方法将力、节点和链接数据限制为相应的力导向布局。通过这种方式,您可以在同一个 SVG 中添加任意数量的布局,而不会造成节点间干扰。每个布局都可以单独格式化。如果您最终希望布局相互影响,您可以编辑它们各自的刻度函数。

function layout1(inputNodes, inputLinks) {
   var force = d3.layout.force();
   var nodes = force.nodes();
   var links = force.links();

   var update = function() {
      //append nodes and links from data

      force.on("tick",function(e){
         //tick movement

      }
   }
   for(var i=0; i<inputNodes.length; i++){
      nodes.push(inputNodes[i]);
   }
   for(var i=0; i<inputLinks.length; i++){
      links.push(inputLinks[i]);
   }
   update();
}

现在第二个强制导向布局可以有相同的结构,以及相同的变量名:

function layout2(inputNodes, inputLinks) {
   var force = d3.layout.force();
   var nodes = force.nodes();
   var links = force.links();

   var update = function() {
      //append nodes and links from data

      force.on("tick",function(e){
         //tick movement

      }
   }
   for(var i=0; i<inputNodes.length; i++){
      nodes.push(inputNodes[i]);
   }
   for(var i=0; i<inputLinks.length; i++){
      links.push(inputLinks[i]);
   }
   update();
}

最后用任何数据实例化:

var layout1 = new layout1(inputNodes, inputLinks);
var layout2 = new layout2(inputNodes, inputLinks);

此方法可用于动态创建多个布局。希望这与您正在寻找的内容接近。

【讨论】:

【解决方案2】:

您可以将 generateMultiForce 函数编写为 jquery 插件 - 这似乎可以保持图形独立并将强制布局应用于两者:

var width = 600,
    height = 600;

var color = d3.scale.category20();

var data = [
  {
    "nodes": [
      {"name": "Hello"},
      {"name": "World"}
    ],
    "links": [
      {"source": 0, "target": 1, "value": 0.5}
    ]
  },
  {
    "nodes": [
      {"name": "Zero"},
      {"name": "One"},
      {"name": "Two"}
    ],
    "links": [
      {"source": 0, "target": 1, "value": 0.25},
      {"source": 1, "target": 2, "value": 0.5},
      {"source": 2, "target": 0, "value": 0.25}
    ]
  }
];

(function( $ ) {
    $.fn.generateMultiForce = function(svgObj) {

        return this.each(function() {
            var graph = this;
            var graphArea = svgObj.append("g");

            var force = d3.layout.force()
                .charge(-200)
                .linkDistance(45)
                .size([width, height])
                .nodes(graph.nodes)
                .links(graph.links)
                .start();

            var link = graphArea.selectAll(".link")
                .data(graph.links)
                .enter().append("line")
                .attr("class", "link");

            var nodeGroup = graphArea.selectAll("g")
                .data(graph.nodes)
                .enter().append("g")
                .call(force.drag);

            var node = nodeGroup.append("circle")
                .attr("class", "node")
                .attr("r", 5)
                .style("fill", function(d) {
                  return color(d.group); });

            var text = nodeGroup.append("text")
                .attr("x", 8)
                .attr("y", ".31em")
                .text(function(d) { return d.name; });

            force.on("tick", function() {
              link
                .attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; });

              nodeGroup.attr("transform", function(d) {
                  return "translate("+d.x+","+d.y+")";
              });
           });
        });
    };
})(jQuery);

var svgTest = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

$(data).generateMultiForce(svgTest);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 2013-08-19
    • 2013-03-21
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    相关资源
    最近更新 更多