fekky Dev
fekky Dev

Reputation: 344

D3 v4 force layout disable animation on drag

How can i disable the animation while dragging the current node in the force simulation in the d3 version 4

Below is the code which is used for drag the node

var node = svg.selectAll(".node")
            .data(json.nodes)
            .enter().append("g")
            .attr("class", "node")
            .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

      function dragstarted(d) {
        if (!d3.event.active) force.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function dragended(d) {
        if (!d3.event.active) force.alphaTarget(0.3);
        // d.fx = null;
        //d.fy = null;
    } 

When i tried to stop the force while in dragged method it doesn't work out. Please suggest me the best way to stop the animation

Please check this fiddle

Upvotes: 2

Views: 2581

Answers (3)

TrickOrTreat
TrickOrTreat

Reputation: 911

Its actually very easy. Simply don't define :

.call(d3.drag().on("start", drag_start)
                 .on("drag", drag_drag)
                 .on("end", drag_end))

This is what is controlling the animation part, and yet you find problem in late loading of your graph due to huge data or something then i suggest add a property called .alphaDecay(0.9), this particular thing will decrease the link length thus decreasing your load time of graph.

Upvotes: 0

Gerardo Furtado
Gerardo Furtado

Reputation: 102174

It's not exactly clear what you mean by "disable the animation". I suppose that you're talking about the movement of the other nodes... well, that's quite the expected behaviour, since you're reheating the simulation.

A possible solution is setting the fx and fy property of all nodes when the simulation ends:

simulation.on("end", function() {
    node.each(function(d) {
        d.fx = d.x;
        d.fy = d.y;
    })
})

Here is your code with that change. Wait until the simulation ends (aprox. 5 seconds) and then drag the nodes:

var nodes = [{
  "id": 1,
  "name": "server 1"
}, {
  "id": 2,
  "name": "server 2"
}, {
  "id": 3,
  "name": "server 3"
}, {
  "id": 4,
  "name": "server 4"
}, {
  "id": 5,
  "name": "server 5"
}, {
  "id": 6,
  "name": "server 6"
}, {
  "id": 7,
  "name": "server 7"
}, {
  "id": 8,
  "name": "server 8"
}, {
  "id": 9,
  "name": "server 9"
}]

var links = [{
  source: 1,
  target: 2
}, {
  source: 1,
  target: 3
}, {
  source: 1,
  target: 4
}, {
  source: 2,
  target: 5
}, {
  source: 2,
  target: 6
}, {
  source: 3,
  target: 7
}, {
  source: 5,
  target: 8
}, {
  source: 6,
  target: 9
}, ]

var index = 10;
var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height"),
  node,
  link;

var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) {
    return d.id;
  }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));

update();

function update() {
  link = svg.selectAll(".link")
    .data(links, function(d) {
      return d.target.id;
    })

  link = link.enter()
    .append("line")
    .attr("class", "link");

  node = svg.selectAll(".node")
    .data(nodes, function(d) {
      return d.id;
    })

  node = node.enter()
    .append("g")
    .attr("class", "node")
    .on("click", click)
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));

  node.append("circle")
    .attr("r", 2.5)

  node.append("title")
    .text(function(d) {
      return d.id;
    });

  node.append("text")
    .attr("dy", 3)
    .text(function(d) {
      return d.name;
    });

  simulation
    .nodes(nodes)
    .on("tick", ticked)
    .on("end", function() {
      node.each(function(d) {
        d.fx = d.x;
        d.fy = d.y;
      })
    })

  simulation.force("link")
    .links(links);
}

function click(d) {
  nodes.push({
    id: index,
    name: "server " + index
  });
  links.push({
    source: d.id,
    target: index
  });
  index++;
  update();
}

function ticked() {
  link
    .attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    });

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

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart()
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
}
.link {
  stroke: #aaa;
}

.node {
  pointer-events: all;
  stroke: none;
  stroke-width: 40px;
  cursor: pointer;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="400"></svg>

Upvotes: 1

Autio
Autio

Reputation: 327

Take a look at this example: http://bl.ocks.org/norrs/2883411 as it seems to accomplish what you are after.

As indicated in the example and the related SO question (D3 force directed graph with drag and drop support to make selected node position fixed when dropped) you will probably be better off creating and using your own drag listener to achieve this specific behaviour.

Upvotes: 0

Related Questions