4

How do I move (drag and drop) multiple shapes with d3.

I tried putting some shapes in a svg and move the svg - this works but not smoothly. This is what I got so far

<html>
<head>
<script type="text/javascript" src="d3.v3.min.js"></script>
<title>Creating SVG groups with D3.js</title>
</head>
<body>
<script type="text/javascript">

// http://tutorials.jenkov.com/svg/g-element.html

d3image = d3.select("body");

svgcanvas = d3image.append("svg:svg").attr("width", 700).attr("height", 500);

svg1 = svgcanvas.append("svg:svg").attr("x", 100).attr("y", 100);

circle1 = svg1.append("svg:circle")
.attr("cx", 40)
.attr("cy", 40)
.attr("r", 37.5)
.call(d3.behavior.drag().on("drag", move));

rect1 = svg1.append("svg:rect")
.attr("x",0)
.attr("y",50)
.attr("width",100)
.attr("height",75)
.call(d3.behavior.drag().on("drag", move));

text1 = svg1.append("svg:text")
.text("Group 1")
.attr("x", 0)
.attr("y", 70)
.style("stroke", "orange")
.style("stroke-width", 1)
.style("font-size", "150%")
.style("fill", "orange")
.call(d3.behavior.drag().on("drag", move));

function move(){
    var parent = d3.select(this.parentNode);
    parent.attr("x", function(){return d3.event.dx + parseInt(parent.attr("x"))})
            .attr("y", function(){return d3.event.dy +             parseInt(parent.attr("y"))});
};

</script>
</body>
</html>

Any suggestions?

Thorsten Niehues
  • 13,712
  • 22
  • 78
  • 113
  • 1
    You could try fixing the position of the svg element, create a group under it and translate the group when any of the object is dragged. In this question you can find more details about this approach: http://stackoverflow.com/questions/7777010/svg-dragging-for-group – Pablo Navarro Jun 04 '13 at 16:13
  • Exactly what I was looking for - Thx a lot. (Unfortunately I did not find this post via Google or the search function). If you post as an Answer I'll accept this as the solution – Thorsten Niehues Jun 05 '13 at 10:43
  • I just posted the comment as an answer. Good luck with your project. – Pablo Navarro Jun 05 '13 at 12:18

2 Answers2

5

The drag behaviour needs to be called on the selection whose position you're updating during the drag. In your code, you are updating the position of the parent node, which causes strange "jittering", because the drag position is itself relative to the parent node.

For example, you could replace your move function above with:

function move() {
  d3.select(this)
      .attr("transform", "translate(" + d3.event.x + "," + d3.event.y + ")");
}
Jason Davies
  • 4,693
  • 30
  • 39
  • OK that explains the bad behavior. But I still need to move all elements in a group (all children of the parent node) – Thorsten Niehues Jun 05 '13 at 10:35
  • 1
    You can make a group draggable by calling the behaviour on the group, i.e. `var g = svg.append("g").call(d3.behavior.drag().on("move", move))`. You can add any number of children to this group. – Jason Davies Jun 05 '13 at 23:38
  • This results in an error - are you sure this is a possible solution? – Thorsten Niehues Jun 06 '13 at 22:08
1

You could try fixing the position of the svg element, create a group under it and translate the group when any of the object is dragged. In this question you can find more details about this approach:

SVG dragging for group

Community
  • 1
  • 1
Pablo Navarro
  • 8,244
  • 2
  • 43
  • 52