Can d3's transition and rotation play well together?

给你一囗甜甜゛ 提交于 2020-01-02 09:59:32

问题


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 rects 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!