2

I'm a beginner to D3/Javascript.

I have a working D3 script where a bunch of path elements are drawn and can be dragged around.

However, when I add seemingly unrelated code elsewhere, that sets the d.x and d.y of the data (to its proper values BTW) the dragging breaks. The element jumps, so that it starts off some distance away and needs to be dragged back to its original place.

(The undesirable "jumping" is orderly way, consistent with a linear transformation of the mouse coordinates)

The "offending" code that seems to cause this behavior is:

hexdata.forEach(function(d) {
        d["x"] = grid_x(d);
        d["y"] = grid_y(d.grid_y);
      });  

The code that constructs the nodes and path that works without the code above is:

var node = svg.selectAll('g') 
        .data(hexdata) 
        .enter()
        .append("g")
        .call(d3.drag()
            .on("drag", dragged))
        .attr("class", "node");


node.append('path') 
    .attr("d", function (d) {
        hex_alignment = (d.grid_y%2==1) ? hexRadius : 0
        return "M" + (d.x *hexRadius*2 + 100)  + "," + ((d.y*1.75 +100)) + hexPath; 
    })

function dragged(d) {
    d3.select(this).attr("transform", "translate(" + d3.event.x + "," + d3.event.y   + ")");
}

Does anyone know what is going on?

  • 1
    in `d3.drag` you need to set `origin` if you are using v3 version. or subject if you are using d3 v4 version. This could be helpful https://stackoverflow.com/questions/38650637/how-to-set-the-origin-while-drag-in-d3-js-in-v4 – Cyril Cherian Sep 07 '17 at 05:25
  • 2
    Great! This seems to work, thank you! All I added was: `.subject(function() { var t = d3.select(this); return {x: t.attr("x"), y: t.attr("y")}; })` Mention of this is absent from all the examples I've seen. Seems like a gotcha, especially since the code is so vacuous. – Courtney Kristensen Sep 07 '17 at 05:32

1 Answers1

2

As mentioned in the comments, setting the origin(v3)/subject(v4) will fix this.

However, if you don't want (for any reason) to set the origin(v3)/subject(v4) in the drag function, simply change the property names for something else, like a and b. We know that x and y is the most common choice for naming the coordinates, but it will cause a conflict in the drag function (whose explanation is beyond the scope here, since you said you are a beginner).

This is easy to show. Here is a simple code using x and y, drag the circle around: it will jump.

var svg = d3.select("svg");
var circle = svg.append("circle")
  .datum({
    x: 150,
    y: 75
  })
  .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
  .attr("r", 10)
  .call(d3.drag()
    .on("drag", dragged))

function dragged(d) {
  d3.select(this).attr("transform", "translate(" + d3.event.x + "," + d3.event.y + ")");
}
svg{
  border: 1px solid gray;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>

Now the same code, using a and b. The circle will not jump:

var svg = d3.select("svg");
var circle = svg.append("circle")
  .datum({
    a: 150,
    b: 75
  })
  .attr("transform", d => "translate(" + d.a + "," + d.b + ")")
  .attr("r", 10)
  .call(d3.drag()
    .on("drag", dragged))

function dragged(d) {
  d3.select(this).attr("transform", "translate(" + d3.event.x + "," + d3.event.y + ")");
}
svg{
  border: 1px solid gray;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>

Another alternative is reassigning d.x and d.y in the drag function:

d3.select(this).attr("transform", "translate(" + (d.x = d3.event.x) + "," 
    + (d.y = d3.event.y) + ")");

Here is the code:

var svg = d3.select("svg");
var circle = svg.append("circle")
  .datum({
    x: 150,
    y: 75
  })
  .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
  .attr("r", 10)
  .call(d3.drag()
    .on("drag", dragged))

function dragged(d) {
  d3.select(this).attr("transform", "translate(" + (d.x = d3.event.x) + "," + (d.y = d3.event.y) + ")");
}
svg{
  border: 1px solid gray;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • Thank you! Do you mind giving a brief explanation of why touching the `x' and `y` properties is so problematic? I also want to learn of any other "under the hood" behavior I should be aware of in D3. – Courtney Kristensen Sep 07 '17 at 21:37
  • Sure. The explanation is a bit complicated because it involves the source code. I'll set up a demo shortly. – Gerardo Furtado Sep 08 '17 at 00:37