Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Stuttering" drag when using d3.behavior.drag() and transform

Tags:

I'm trying to use d3.js's d3.behavior.drag() drag event to update my data model (without setting the element position immediately), then run my "draw" function to update all elements based on the updated model. Additionally, I'm using a translate transform on the containing group element in order to move all the elements associated with the dragged object (I removed the extra elements from the example linked below). This causes the dragged elements to stutter as it's dragged, which gets worse the faster you drag it.

See this stripped-down example on jsFiddle.

Here's the code of the example:

blocks = [
  { x: 0, y: 0 }
];

drag = d3.behavior.drag()
  .origin(Object)
  .on("drag", function(d) {
    d.x = d3.event.x;
    d.y = d3.event.y;
    draw();
  });

svg = d3.select("body")
  .append("svg:svg")
  .attr("width", 600)
  .attr("height", 600);

function draw() {
  g = svg.selectAll("g")
    .data(blocks);

  gEnter = g.enter().append("g");

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

  gEnter.append("rect")
    .attr("height", 100)
    .attr("width", 100)
    .call(drag);
}

draw()​;
like image 978
tlrobinson Avatar asked Oct 25 '12 22:10

tlrobinson


3 Answers

You are calling drag on the rect element, but you're transforming its parent: the g element.

The problem is that the drag component uses the mouse location relative to the parent to determine the new d3.event.x and d3.event.y. So, if you move (i.e. transform) the parent while the user drags, things get messed up.

The solution is to call drag on the same element that you are moving around; in this case the g element:

function draw() {
  g = svg.selectAll("g")
    .data(blocks);

  gEnter = g.enter().append("g")
    .call(drag);

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

  gEnter.append("rect")
    .attr("height", 100)
    .attr("width", 100);
}

See corrected Fiddle: http://jsfiddle.net/EMNGq/14/

like image 71
nautat Avatar answered Feb 01 '23 23:02

nautat


I had the same issue as Darwin. The drag handler is on the child object that has D3 Data attached but the transform needs to be applied to the parent group (not d3).

I solved it by setting the origin function to return x:0, y:0 and then using the x value in the event rather than the dx.

e.g

var drag = d3.behavior.drag()
    .origin(function(d,i) { return {x:0, y:0}; })
    .on("drag", function (d) {  movePosition(d3.event.x);   });
like image 32
avowkind Avatar answered Feb 02 '23 01:02

avowkind


Nautat provided good solution for your case.

My case is slightly more complicated. My data is bound below g-node I want to transform. In this case I cannot simply move .call(drag) to g-level.

I submitted a pull request to make base coordinate space customisable: https://github.com/mbostock/d3/pull/1055

like image 23
Antonin Hildebrand Avatar answered Feb 02 '23 01:02

Antonin Hildebrand