简体   繁体   中英

D3 graph with svg nodes - How to move nodes to arbitrary positions

I want to make a D3 graph, which should be as follows:

  1. When the html page is loaded, there will be a single node at a fixed location. Let us say top left. Let us call it template node and this node is non-movable.

  2. When the user does mouse down on the template node, a new node is created at the same location as the template node and the user should be able to drag the new node to where he wants. The new node should remain exactly where the user moves it to.

  3. At any time user should be able to move a node. Again the node should remain where the user leaves it.

  4. User should be able to draw link between any two nodes. Let us assume that if he drags from one node to another without holding down ctrl key, then a link is drawn and if he drags while holding down the control key, then the node moves.

  5. When a link is drawn between two nodes, then the nodes should not change positions.

  6. When two nodes are linked and one of them is moved by dragging it, then the link should change in size and orientation as needed.


I am using force layout.

I am able to create a template node but it always goes to the center of the container - I think it is because the center of the container is the center of gravity. But not sure how to fix its position to the top left through code.

I can create links and new nodes. But the nodes move and links resize. May be it is because force layout tries to make link lengths equal to the link distance in the force layout. But I do not know how to use a function for link distance? I am even not sure if that will really help.

So what method should I use? Any idea?

For force layout, you can set the 'fixed' property of a node to true in order to prevent it from being affected by the simulation. After that, you should be able to set it's position manually. You might choose to do this in a function call:

function pinNode(node) {
  node.fixed = true;
}

function unpinNode(node) {
  node.fixed = false;
}

I believe you could get a node to the upper left corner with a call like this: pinNode(node, 0, 0) . As long as the node has its fixed property set to true, it should remain unaffected by the sim. You might find this snippet from the documentation helpful; it describes how the fixed property is affected by force.drag:

Bind a behavior to nodes to allow interactive dragging, either using the mouse or touch. Use this in conjunction with the call operator on the nodes; for example, say node.call(force.drag) on initialization. The drag event sets the fixed attribute of nodes on mouseover, such that as soon as the mouse is over a node, it stops moving. Fixing on mouseover, rather than on mousedown, makes it easier to catch moving nodes. When a mousedown event is received, and on each subsequent mousemove until mouseup, the node center is set to the current mouse position. In addition, each mousemove triggers a resume of the force layout, reheating the simulation. If you want dragged nodes to remain fixed after dragging, set the fixed attribute to true on dragstart, as in the sticky force layout example.

force.drag

Also see here: force layout nodes

If you want to use a function for link distance, include it when you create the force layout:

var force = d3.layout.force()
    .size(width, height)
    .linkStrength(0.5) // how much can link distance be overridedn by the simulation
    .linkDistance(function() {return /* some evaluation */;});

// ...

// You might need to defer the calculation of linkDistance until later,
// such as in update(), since nodes might not have the properties
// that you need to check until that point:

function update() {
  force
    .nodes(nodes)
    .links(links)
    .linkDistance(function(link) {
      // The function gets called for each link in the simulation.
      // Each link will be connected to two nodes, source and target, 
      // which may be useful in determining link distance.
      if (link.source.someProperty || link.target.somePropery) {
        return /* something */;
      } else {
        return /* something else */;
      }
    });
}

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