【问题标题】:D3 force layout with CSS positioning带 CSS 定位的 D3 强制布局
【发布时间】:2013-12-02 08:48:20
【问题描述】:

我在地图上放置了与 GPS 坐标相对应的圆圈。每个圆圈都包含在一个 svg 容器中,该容器使用 CSS 顶部和左侧属性放置在页面上。在我的实现中,这些容器经常相互叠放。

我正在尝试实现碰撞检测和/或向这些容器添加轻微的负电荷,以便重叠导致容器彼此远离。

到目前为止,我对强制布局的测试要么没有变化,要么导致错误(“无法设置 null 的属性索引”或“无法设置 null 的属性 x”)。很明显我做错了什么,但我无法从我在网上阅读的文章中找出解决问题的方法。

关于如何阻止容器相互叠放有什么想法吗?

var self = this;
var data = [{lat: 127, lon: 36, name: 'a', radius: 9},{lat:127, lon: 36, name: 'b', radius: 9}];

// Position SVG containers correctly
var latLngToPx = function(d) {
  var temp = new google.maps.LatLng(d.lat, d.lon);
  temp = self.map.projection.fromLatLngToDivPixel(temp);
  d.x = temp.x;
  d.y = temp.y;
  return d3.select(this)
    .style('left', d.x + 'px')
    .style('top', d.y +  'px');
};

var collide = function(node) {
  var r = node.radius + 16,
      nx1 = node.x - r,
      nx2 = node.x + r,
      ny1 = node.y - r,
      ny2 = node.y + r;
  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
          y = node.y - quad.point.y,
          l = Math.sqrt(x * x + y * y),
          r = node.radius + quad.point.radius;
      if (l < r) {
        l = (l - r) / l * 0.5;
        node.x -= x *= l;
        node.y -= y *= l;
        quad.point.x += x;
        quad.point.y += y;
      }
    }
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
  };
}  

var svgBind = d3.select(settings[type].layer).selectAll('svg')
  .data(data, function(d){ return d.name; })
  .each(latLngToPx);

var svg = svgBind.enter().append('svg')
  .each(latLngToPx)

// svg[0] contains the svg elements
var nodes = svg[0];
var force = d3.layout.force()
  .nodes(nodes)
  .charge(-100)
  .start();

force.on('tick', function(){
  var q = d3.geom.quadtree(nodes),
  i = 0,
  n = nodes.length;

  while (++i < n) {
    q.visit(collide(nodes[i]));
  }

   svg
     .style('left', function(d){ return (d.x - lm.config.offset) + 'px';})
     .style('top', function(d){ return (d.y - lm.config.offset) + 'px';});
});

var circ = svg.append('circle')
    .attr('r', settings[type].r)
    .attr('cx',10)
    .attr('cy',10)

【问题讨论】:

    标签: svg d3.js collision-detection force-layout


    【解决方案1】:

    您不需要自己进行碰撞检测——力布局应该会为您处理好。以下是您需要采取的基本步骤。

    • 向每个表示圆的数据元素添加包含其当前(屏幕)坐标的xy 成员。这就是力布局的作用。
    • 将这些元素的数组作为节点传递给强制布局。开始时无需设置链接,但您可能希望稍后再设置以控制节点相对于彼此的位置。
    • 开始强制布局。
    • 对于每个刻度,在适当的位置重新绘制元素。
    • 根据自己的喜好调整力布局的参数。

    您已经完成了大部分工作,我只是再次提及以澄清一下。代码看起来像这样。

    function latLngToPx(d) {
      var temp = new google.maps.LatLng(d.lat, d.lon);
      temp = self.map.projection.fromLatLngToDivPixel(temp);
      d.x = temp.x;
      d.y = temp.y;
    };
    data.forEach(function(d) { latLngToPx(d); });
    
    var nodes = d3.select("body").selectAll("svg").data(data).enter().append("svg");
    
    var force = d3.layout.force().nodes(data);
    force.on("tick", function() {
      nodes.style('left', function(d){ return (d.x - lm.config.offset) + 'px';})
           .style('top', function(d){ return (d.y - lm.config.offset) + 'px';});
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-23
      • 1970-01-01
      • 1970-01-01
      • 2016-08-15
      • 2018-12-24
      • 2014-05-17
      • 2014-07-09
      相关资源
      最近更新 更多