代码之家  ›  专栏  ›  技术社区  ›  Stav Alfi

d3。js-强制冲突布局

  •  0
  • Stav Alfi  · 技术社区  · 7 年前

    我正在努力实现 following script 在v4中:

    此脚本是用v3编写的,在v4中无效。 (我也是d3新手,开始学习v4)

    var width = 400,
        height = 400;
    
    var nodes = d3.range(200).map(function() { return {radius: Math.random() * 12 + 4}; }),
        root = nodes[0],
        color = d3.scale.category10();
    
    root.radius = 0;
    root.fixed = true;
    
    var force = d3.layout.force()
        .gravity(0.05)
        .charge(function(d, i) { return i ? 0 : -2000; })
        .nodes(nodes)
        .size([width, height]);
    
    force.start();
    
    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height);
    
    svg.selectAll("circle")
        .data(nodes.slice(1))
      .enter().append("circle")
        .attr("r", function(d) { return d.radius; })
        .style("fill", function(d, i) { return color(i % 3); });
    
    force.on("tick", function(e) {
      var q = d3.geom.quadtree(nodes),
          i = 0,
          n = nodes.length;
    
      while (++i < n) q.visit(collide(nodes[i]));
    
      svg.selectAll("circle")
          .attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
    });
    
    svg.on("mousemove", function() {
      var p1 = d3.mouse(this);
      root.px = p1[0];
      root.py = p1[1];
      force.resume();
    });
    
    function collide(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 * .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;
      };
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

    我能想到的最佳解决方案是绕着一个大的特殊圆圈移动,并使用碰撞来避免其他圆圈接触我的大圆圈。

    但我的大圈子都冻住了。 此外,我认为我的整体解决方案不太好:

    我做错了什么?

    var width = 1000,
        height = 600;
    
    d3.select("body")
        .append("svg")
        .attr("width", 1000)
        .attr("height", 600);
    
    const collideCircle = {
        radius: 30,
        fill: 'black',
        center: {
            x: 200,
            y: 200
        }
    };
    
    const radius = 10;
    const moreCircles = [...Array(40).keys()].map(i => ({
        radius,
        fill: 'pink',
        center: {
            x: 200,
            y: 200
        }
    }));
    
    const simulation = d3.forceSimulation([collideCircle, ...moreCircles])
        .force('x', d3.forceX().x(d => d.center.x))
        .force('y', d3.forceY().y(d => d.center.y))
        .force('collision', d3.forceCollide().radius(d => d.radius))
        .on('tick', ticked);
    
    const collideCircles = d3.select('svg')
        .append('g')
        .selectAll('circle')
        .data([collideCircle])
        .enter().append('circle')
        .attr('fill', d => d.fill)
        .attr('r', d => d.radius)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y);
    
    const moreCirclesGeom = d3.select('svg')
        .append('g')
        .selectAll('circle')
        .data(moreCircles)
        .enter().append('circle')
        .attr('fill', d => d.fill)
        .attr('r', d => d.radius)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y);
    
    function ticked() {
        // stay in the SVG (box) only:
    
        moreCirclesGeom
            .attr("cx", function (d) {
                return d.x = Math.max(radius, Math.min(width - radius, d.x));
            })
            .attr("cy", function (d) {
                return d.y = Math.max(radius, Math.min(height - radius, d.y));
            });
    
        collideCircles
            .attr("cx", function (d) {
                return d.x = Math.max(radius, Math.min(width - radius, d.x));
            })
            .attr("cy", function (d) {
                return d.y = Math.max(radius, Math.min(height - radius, d.y));
            });
    }
    
    d3.select('svg').on("mouseover", handleMouseOver);
    
    function handleMouseOver() {
        collideCircle.fx = d3.event.x;
        collideCircle.fy = d3.event.y;
    
        simulation.alphaTarget(0.1).restart();
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
    0 回复  |  直到 7 年前