1

我正在尝试在 D3 中获得一个力布局图(如http://bl.ocks.org/mbostock/1748247),以便与反应性数据源(meteor.js)配合使用。我对这两个世界都比较陌生,我遇到了这个问题。我已经使力布局正常工作,并将数据源设置为我的流星集合效果很好,但是当我从控制台更新或添加到数据库时,所有节点都像刚刚生成一样飞来飞去。就好像所有数据都被视为新数据,而不是仅附加新数据或转换当前节点以匹配更新。

我已经完成了所有关于 D3 和流星的讨论,但是我对这些概念的理解还不够强大,无法进一步了解。感谢任何帮助或正确方向的观点。

我的 d3 区域模板包含在 #constant 中,如下所示:

<template name="ideaspace">
{{#constant}}
 <svg>
</svg>
{{/constant}}   
</template>

然后在客户端我得到了这个。(请原谅糟糕的代码。我在这里的兔子很远,只是尝试我遇到的一切,看看我是否能得到一些线索)

Template.ideaspace.rendered = function () {  
var self = this;
self.node = self.find("svg");

if(! self.handle) {
    self.handle = Deps.autorun(function () {
//d3 code
var nodes =  DataPoints.find().fetch();
var force = d3.layout.force()
      .nodes(nodes)
      .size([width, height])
      .gravity(.02)
      .charge(0)
      .on("tick", tick)
      ;  
     var svg = d3.select("svg")
      .attr("width", width)
      .attr("height", '100%')
      .attr("id", 'container')
      .style("top", '30px')
      .style("position", 'fixed')
      .attr("pointer-events", "all")

  svg.append("rect")
        .attr("width", "100%")
        .attr("height", "100%")
        .attr("fill", "whitesmoke")
        .call(d3.behavior.zoom()
          .on("zoom", function() {
            scale = d3.event.scale;
            if (scale<=1){
              width = $(window).width()/d3.event.scale;
              foci = [{x: '0%', y: 150}, {x: width*1/4, y: 1/2*height}, {x: width*2/4, y: 1/2*height}, {x: width*3/4, y: 1/2*height}];
            }                        
            svg.attr("transform", "translate(" + d3.event.translate + 
              ")scale(" + d3.event.scale + ")");
          }));    

  var x =
    d3.scale.linear()
      .domain([0, width])
      .range([0, width]);

  var y =
    d3.scale.linear()
      .domain([0, height])
      .range([0, height]);

  var bubble = svg.selectAll(".bubble");

    bubble = bubble.data(nodes,function (party) { return party._id; });
     width = $(window).width();

    bubble.enter().append("svg:circle")
      .style("fill", function(d) { return color(+d.emperical); })
      .style("stroke", "grey")
      .attr("id", function(d) { return d.objectId+"_c"; })
      .attr("r", function(d) { return d.radius; })
      .call(force.drag);

    bubble.transition().duration(5000);
    force.start();

    function tick(e) {
      bubble
         .each(collide(.5))
         .attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")";
      })              
  }
function cluster(alpha) {
      var max = {};

      // Find the largest node for each cluster.
      nodes.forEach(function(d) {
        if (!(d.emperical in max) || (d.radius > max[d.emperical].radius)) {
          max[d.emperical] = d;
        }
      });

      return function(d) {
        var node = max[d.emperical],
            l,
            r,
            x,
            y,
            i = -1;

        if (node == d) return;

        x = d.x - node.x;
        y = d.y - node.y;
        l = Math.sqrt(x * x + y * y);
        r = d.radius + node.radius;
        if (l != r) {
          l = (l - r) / l * alpha;
          d.x -= x *= l;
          d.y -= y *= l;
          node.x += x;
          node.y += y;
        }
      };
    }
  })
};

};

更新:这使添加新数据正常工作。现在我想我需要构建一些东西来检查是否有任何其他字段被更改。

        self.handle = Deps.autorun(function () {       

        var topicPoints = TopicPoints.find().fetch();   
        var found = true;
        topicPoints.forEach(function (d) {              
            if(nodes.length>0){                             
            for(var i = 0; i < nodes.length; i++) {
                if (nodes[i]._id == d._id) {
                    console.log("Found "+ d._id)
                    found = true;
                    break;
                } else {
                    found = false
                    console.log("Not Found "+ d._id)
                }
            }
            if(!found){                     
                nodes.push({
                _id:d._id,
                title: d.title,
                radius: d.radius
                });     
            }

            } else {
                nodes = topicPoints;
            }


        });

        render(nodes);

    })
4

1 回答 1

3

问题是您正在nodesDatapoints.find().fetch(). 该数组(可能)缺少以下属性:

  • index - 节点数组中节点的从零开始的索引。
  • x - 当前节点位置的 x 坐标。
  • y - 当前节点位置的 y 坐标。
  • px - 前一个节点位置的 x 坐标。
  • py - 前一个节点位置的 y 坐标。
  • fixed - 一个布尔值,指示节点位置是否被锁定。
  • weight - 节点权重;关联链接的数量。

调用这些属性并不是严格需要的force.nodes(),但如果这些属性不存在于数组中,那么它们将在第一次调用时被随机初始化。force.start()这就是你所见证的。

因此,解决方案是重用单个数组,该数组在调用nodes中保留。rendered当您必须更新数据时,自己将数组与新数据合并(可能使用一组party._id或一个聪明的数据库查询而不是.find().fetch())。这将保留现在设置了上述属性的旧对象,并插入不具有这些属性的新元素。然后新传入的对象将在调用后被随机放置和同化force.start()

这也使您可以更灵活地插入更新。例如,如果您希望新对象始终从左角进入,则在合并期间,您可以为新对象设置xand yto 0

于 2013-06-09T12:32:24.760 回答