问题
I'm in the early stages of a JS project. Everything is going fine so far except for the positioning of one shape. The shape in question is a teal diamond (square rotated 45 degrees). I can get the square on the screen no problem, but when I add:
.attr("transform", "rotate(45)")
the square rotates properly, but shifts towards the left portion of the screen, like this:
I am not sure what is causing this to happen. If it helps, here is some of the code that produced 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 put the "y" attribute in, the square disappears completely.
What is causing this? Did I do something wrong that I just can't see?
回答1:
When you rotate the rect, you also rotate its coordinate system. So when you later move it 250 along the x axis, you're actually moving it 250 units along a 45 degree axis -- the result of the rotation.
As a rule, when you introduce a transform attribute as you did for rotate, you should do all your transformation via this attribute. So, you need to use translate instead of using the "x" attribute. Then it would look like this:
svg
.append("rect")
.attr("transform", "translate(250, 0) rotate(45)")
// remove this: .attr("x", 250)
.attr("height", w / 10)
...
This gets you the results I think you're looking for. Now note that the order of transformations matters here: if your transform was "rotate(45) translate(250, 0)" (i.e. first rotate then translate), you'd get the same, wrong results you got before. That's because when you rotate first, your translation happens, as before, along a rotated x axis.
回答2:
In SVG you must set the transform origin to get it to rotate from the center, such as...
.attr("transform", "rotate(45, 250, 100)");
Where 250, 100 is the x and y position of your rect minus it's radius. Putting it all together it would look like...
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")
You can view a demo here:
http://jsfiddle.net/uwM8u/
回答3:
Here is an approach slightly different from the answer Duopixel gave. Here you are not repeating the calculations for X and Y. In Duopixel's example, its a trivial improvement since he is merely referencing a structure. Its often the case that X and Y are functions and I would not want to maintain that loging in two places. This approach allows you to set X and Y a node, then rotate on the center of said node.
You may find that after the rotation, you still want to tweak the final position, which could be done with another transform, 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 + ")";
})
来源:https://stackoverflow.com/questions/10985077/d3-js-odd-rotation-behavior