简体   繁体   中英

D3js Force Layout barrier and fall in

I'm working a force layout. I've already implemented collisions and bounding within the svg (I don't want to use gravity). However, now I want to place a div in the middle and have the nodes avoid it.

The approach I'm attempting is overlaying the SVG on top of the div using absolute positions and attempting to have the nodes avoid it in the tick function. This is proving to be very difficult. Is there another approach? If not, I'd greatly appreciate a point in the right direction!

As a sort of secondary question, I'd love to have the nodes fall in from the top. Once again, I don't have gravity, so I'm not really sure how to approach that. I'd like them to maintain their spacing, so I think a focus may not work.

I've attached my Javascript and CSS code!

Thanks in advance.

    width = parseInt(d3.select('#canvas').style('width'), 10),
    height = parseInt(d3.select('#canvas').style('height'), 10),
    padding = 30, // separation between circles
    radius = 70;

    //Set up the force layout
    var force = d3.layout.force()
        .gravity(0)
        .charge(0)
        .size([width, height]);

    //Append a SVG to the body of the html page.
    //Assign this SVG as an object to svg
    var svg = d3.select("#canvas").append("svg")
        .attr("width", width)
        .attr("height", height);

    //Read the data
    d3.json("dots.json", function(error, graph) {
        if (error) throw error;

        //Creates the graph data structure out of the json data
        force.nodes(graph.nodes)
            .start();


        //Do the same with the circles for the nodes - no
        var node = svg.selectAll(".node")
            .data(graph.nodes)
        .enter().append("g")
            .attr("class", "node")
            .call(force.drag);

        node.append("circle")
          .attr("r", radius)
            .style("fill", "black")
          .call(force.drag);

        node.append("text")
          .attr("dx", 0)
          .attr("dy", 5)
          .attr("height", 10)
          .attr("text-anchor", "middle")
          .text(function(d) { return d.name })
          .style("fill", "white");


        //Now we are giving the SVGs co-ordinates - the force layout
        //is generating the co-ordinates which this code is using to
        //update the attributes of the SVG elements
        force.on("tick", function (e) {

            node.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));
            });

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


            node.each(collide(0.5)); //Added
        });

        function collide(alpha) {
          var quadtree = d3.geom.quadtree(graph.nodes);
          return function(d) {
            var rb = 2*radius + padding,
                nx1 = d.x - rb,
                nx2 = d.x + rb,
                ny1 = d.y - rb,
                ny2 = d.y + rb;

            quadtree.visit(function(quad, x1, y1, x2, y2) {
              if (quad.point && (quad.point !== d)) {
                var x = d.x - quad.point.x,
                    y = d.y - quad.point.y,
                    l = Math.sqrt(x * x + y * y);
                  if (l < rb) {
                  l = (l - rb) / l * alpha;
                  d.x -= x *= l;
                  d.y -= y *= l;
                  quad.point.x += x;
                  quad.point.y += y;
                }
              }
              return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
            });
          };
        }

        node.on("mousedown", function(d1, i) {
            var $this = d3.select(this);
            if(node.active) {
                $this = $this.transition()
                    .duration(1000)
                    .attr("stroke-width", 0)
                    .attr("r", radius);

                node.active = false;
            } else {
                $this = $this.transition()
                    .duration(1000)
                    .attr("stroke-width", 20)
                    .attr("stroke", "4DD2B6")
                    .attr("fill", "white")
                    .attr("r", radius * 2)
            }
        });

    });



#canvas, #playground {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1;
    overflow: visible;
    display: block;
}

#playground {
    height: 100%;
}

header {
    width: 320px;
    margin: 100px auto 50px;
    display: block;
    z-index: 10;
    background-color: red;
}

屏幕

Boundaries

The hard part about collision detection is the fact that each node could collide with any other node and that's why the quad tree is used to reduce the permutations. When you are talking about a boundary it's much more straight forward, because there is only one boundary. All you need to do in your case is extend the geometry of the boundary to include the occlusion.

Fall in

It depends what you mean exactly, but you can set the initial cx/cy values to the source from which you want them to emanate from and then extend your boundary behaviour to include what to do if the nodes are outside of the svg. So that would be some kind of custom gravity to determine the next cx/cy based on current position and a direction you want them to fall in. Meanwhile, the collision routine is not aware of boundaries so, it will take care of keeping the nodes apart as they fall.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM