D3.js Node "bounces" from fast drag

I am using force layout with zero gravity and charge value:

var force = d3.layout.force()
    .gravity(0)
    .charge(0)
    .friction(0.9)
    .linkDistance(250)
    .linkStrength(1)
    .size([width, height])
    .on("tick", tick);

function tick(e) {
    d.y = Math.max(d.radius, Math.min(height - d.radius, d.y));
    d.x = Math.max(d.radius, Math.min(width - d.radius, d.x));

    return "translate(" + d.x + "," + d.y + ")";
});
        }

      

There is a circle. And the problem is, when I do a quick drag, the circle "bounces". Any help would be greatly appreciated. Thank.

Codepen: http://codepen.io/vanquang9387/pen/gpLpGE?editors=001

+3


source to share


1 answer


Dirty fix

Change this ...

.friction(0.9)

      

to that...

.friction(0)

      

Background

Inside the force compositing module in d3 there is a method force.tick

that is called before each animation frame. In this case, the node positions are recalculated. There is a calculation for the links that link strength

, weights

and the target linkDistance

; a gravity

calculation that is a function of the distance of each node from the center of the layout; and calculating the charge, which is based on charge

, chargeDistance

and the relative position of the nodes. There is also a calculation for friction

. All of this - apart from the calculation friction

- takes into account the current "temperature" of the layout ( alpha

), which is really just an exponentially decreasing value, which is a function of the number of ticks that have passed since the layout was started.

These calculations are applied sequentially to all elements of the layout, with the input positions for each step being the output from the previous one. But "fixed" nodes are handled differently for friction calculation, and dragged nodes are handled fixed

for resistance.



friction

is not really "friction", it is more like decay of speed, as explained in WIKI , the computation is friction

designed to maintain the speed of the nodes by moving them from their position at the end of the previous tick ( px

, py

). The distance moved away is proportional to the speed of each node, which is based on the distance between ( px

, py

) and the position calculated by the previous steps (links, charge and gravity) in the current tick

(actually this is a little more complicated because the calculation of the charge is actually "not caused" and changes the previous positions, but this does not affect the principle of calculating friction).

During dragging, ( px

, py

) is updated by the mouse position with the drag behavior on each mousemove

. Then on the next tick these values ​​are copied to ( x

, y

) in the force layout. So, while dragging, the previous position is actually the current position and vice versa! So when the drag ends, the speed used in calculating the friction is in the opposite direction to the actual speed, so friction

calc tries to maintain that ... and why it bounces back.

Dead stop

The next step is to find a way to set ( px

, py

) to ( x

, y

) in an event handler dragend

.
Like this, for example ...

  var stdDragEnd = force.drag().on("dragend.force");
  force.drag().on("dragend.force", myDragEnd);
  function myDragEnd(d) {
    d.px = d.x; d.py = d.y;
    stdDragEnd.call(this, d)
  }

      

You can put this anywhere in your code where your variable is already defined force

. The idea is to intercept the standard behavior, not replace it.

Now even if you set friction to 1.0 the node will stop after dragging.
You don't need to keep the context this

based on the current state of the code, but it's good practice anyway, I think. Who knows what the future will bring lah;)

+4


source







All Articles