D3 SVG transform rotation transition behaving weir

2019-01-29 09:27发布

问题:

I generated a geometric SVG pattern with D3 and I wanted to animate it. I wanted to have the circles and square spinning around their centre. However when they rotate the centre point moves along an elliptical path before returning to the correct position.

My codepen is here: http://codepen.io/andybarefoot/pen/bwkjaN Clicking "spin" in the top left corner shows the unusual behaviour. My key function for rotating the "group" looks like this:

    function spinCircles() {
        d3
            .select("#fixedGroup")
            .attr("transform", "translate (0, 0) rotate(0)")
            .transition()
            .duration(4000)
            .attr("transform", function(d, i){
                return "translate (0, 0) rotate (90,"+m/2+","+m/2+")";
            })
        ;
    }

I suspect the complexity arises from the fact that I have nested SVG elements with different viewboxes as I wanted my SVG to be "responsive". (You can resize the window and the diagonal lines will change angle to continue aligning with the corners of the screen, whilst the circles and square keep the same aspect ratio.)

The code for the nested SVGs looks like this:

        var baseSVG = d3.select("body")
            .append("svg")
            .attr("id", "baseSVG")
        ;
        var stretchSVG = baseSVG
            .append("svg")
            .attr("id", "stretchSVG")
            .attr("viewBox", "0 0 " + w + " " + h)
            .attr("preserveAspectRatio", "none")
        ;
        var fixedSVG = baseSVG
            .append("svg")
            .attr("id", "fixedSVG")
            .attr("viewBox", "0 0 " + m + " " + m)
        ;

However I'm also open to the fault lying with my lousy understanding of transfer transitions in d3...

Any help very gratefully received!

回答1:

For D3 to rotate around a given center, you need to provide a custom tween function which can make use of a string interpolator:

function spinCircles() {
  d3
    .selectAll("#fixedGroup")
    .transition()
    .duration(4000)
    .attrTween("transform", function() {
      var center = "" + m/2 + "," +  m/2;
      return d3.interpolateString("rotate(0," + center + ")", "rotate(90," + center + ")");
    });
} 

Have a look at the updated codepen for a working example.