D3.js Odd rotation behavior

I am in an early stage of the JS project. So far, everything is going well, except for positioning one piece. The sample in question is a teal diamond (square rotation of 45 degrees). I can get a square on the screen without problems, but when I add:

.attr("transform", "rotate(45)") 

the square rotates correctly, but moves to the left of the screen, for example:

enter image description here

I am not sure what causes this. If this helps, here are some of the code that gave this result:

 var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg .append("rect") .attr("transform", "rotate(45)") .attr("x", 250) .attr("height", w / 10) .attr("width", w / 10) .attr("fill", "teal") 

Note. If I insert the attribute "y", the square disappears completely.

What causes this? I did something wrong that I just can’t see?

+8
javascript
source share
3 answers

When you rotate a rectangle, you also rotate its coordinate system. Therefore, when you later move 250 along the x axis, you are actually moving 250 units along the 45 degree axis - the result of rotation.

Typically, when you enter the transform attribute, as with rotate , you must do all your transforms with this attribute. Thus, you need to use translate instead of using the attribute "x" . Then it will look like this:

 svg .append("rect") .attr("transform", "translate(250, 0) rotate(45)") // remove this: .attr("x", 250) .attr("height", w / 10) ... 

This gives you the results that I think you are looking for. Now notice that the order of the transforms matters here: if your transform was "rotate(45) translate(250, 0)" (i.e., rotate first, then translate), you will get the same incorrect results that you received earlier. This is because when you first spin, your translation occurs, as before, along the x axis.

+15
source share

In SVG, you must set the transform source to make it rotate from the center, for example ...

 .attr("transform", "rotate(45, 250, 100)"); 

Where 250, 100 is the x and y position of your rectangle, minus its radius. Putting it all together, it will look ...

 var svg = d3.select("body") .append("svg") .attr("width", 400) .attr("height", 300); svg .append("rect") .attr("transform", "rotate(30,"+ (diamond.x+diamond.width/2) + ","+ (diamond.y+diamond.width/2) +")") .attr("x", diamond.x) .attr("y", diamond.y) .attr("height", diamond.width) .attr("width", diamond.width) .attr("fill", "teal")​ 

Here you can see the demo:

http://jsfiddle.net/uwM8u/

+11
source share

Here is an approach slightly different from the answer that Duopixel gave. Here you are not repeating the calculations for X and Y. In the Duopixel example, its trivial improvement, since it just refers to the structure. Often this happens when X and Y are functions, and I would not want this logging in two places. This approach allows you to set X and Y a node, and then rotate to the center of the specified node.

You may find that after the rotation you still want to adjust the last position that could have been done using another conversion, or in the case of TEXT, you can use dx, dy.

  svgNode.attr("transform", function (d) { var w = +d3.select(this).attr("x") + (this.getBBox().width / 2) ; var h = +d3.select(this).attr("y") + (this.getBBox().height / 2); return "rotate(90," + w + "," + h + ")"; }) 
0
source share

All Articles