问题
Transitions in combination with rotations have odd results.
Here is a fiddle with my problem: http://jsfiddle.net/emperorz/E3G3z/1/ Try clicking on each square to see the varying behaviour.
Please forgive the hacked code, but if I use transition with rotation (and x/y placement) then it loops about.
I have tried:
1) all in the transform (rotate then translate), and that seems mostly okay. A little wobbly.
2) just rotate in the transform, positioned using x/y attributes. Flies all over the place, but ends up at the correct spot. Very weird.
3) all in the transform (translate then rotate), flies away, and ends up in the (completely) wrong place.
Hmmm. Strange.
Is there a correct approach to rotating shapes with transitions?
Intuitively, it would be good if the second option worked.
Thanks
回答1:
To rotate an SVG object on an arbitrary axis, you need two transformations: translate
(to set the axis) and rotate
. What you really want is to apply the translate
fully first and then rotate the already moved element, but it appears that translate
and rotate
operate independently and simultaneously. This ends at the right place, but animating the translate
is essentially moving the axis during rotation, creating the wobble. You can isolate the translate
from the rotate
by having them occur at separate places in the SVG element hierarchy. For example, take a look at the following:
<g class="outer">
<g class="rect-container">
<rect class="rotate-me" width=200 height=100 />
</g>
</g>
You can center the <rect>
on (0,0) with translate (-100, -50)
. It will wobble if you apply your rotation to the <rect>
element, but it will rotate cleanly if you rotate
the g.rect-container
element. If you want to reposition, scale, or otherwise transform the element further, do so on g.outer
. That's it. You now have full control of your transforms.
Finding a <rect>
's center is easy, but finding the center of a <path>
, <g>
, etc. is much harder. Luckily, a simple solution is available in the .getBBox() method (code in CoffeeScript; see below for a JavaScript version*):
centerToOrigin = (el) ->
boundingBox = el.getBBox()
return {
x: -1 * Math.floor(boundingBox.width/2),
y: -1 * Math.floor(boundingBox.height/2)
}
You can now center your element/group by passing the non-wrapped element (using D3's .node()
method)
group = d3.select("g.rotate-me")
center = centerToOrigin(group.node())
group.attr("transform", "translate(#{center.x}, #{center.y})")
For code that implements this on a both a single <rect>
and <g>
of of 2 rect
s with repositioning and scaling, see this fiddle.
*Javascript of the above code:
var center, centerToOrigin, group;
centerToOrigin = function(el) {
var boundingBox;
boundingBox = el.getBBox();
return {
x: -1 * Math.floor(boundingBox.width / 2),
y: -1 * Math.floor(boundingBox.height / 2)
};
};
group = d3.select("g.rotate-me");
center = centerToOrigin(group.node());
group.attr("transform", "translate(" + center.x + ", " + center.y + ")");
回答2:
iirc translate is relative to 0,0 whereas rotate is around the center point of the object
As such, because your shapes are offset from 0,0 (e.g. 100,200, or 200,100) they end up migrating when translated. This can be seen by changing the offsets for Diamond3 to [50,50] - much smaller migration around the screen
The solution would be rebase the 0,0 point to the center of the diamond. There is a way to do this in D3 - but I can't remember what it is off the top of my head :(
来源:https://stackoverflow.com/questions/12494447/can-d3s-transition-and-rotation-play-well-together