代码之家  ›  专栏  ›  技术社区  ›  tic

如何在D3中获得完整的顺时针旋转

  •  1
  • tic  · 技术社区  · 7 年前

    我想要一个正方形在半个旋转暂停后,完成自身的完全顺时针旋转。 下面的代码使它顺时针旋转一半,逆时针旋转另一半,这与我的预期相反。

    var svg = d3.select('svg');
    
    var s = svg.append("rect")
      .attr("width", 50)
      .attr("height", 50)
      .attr("x", -25)
      .attr("y", -25)
      .attr("fill", "red")
      .attr("transform", "translate(100,100)");
    
    s
      .transition()
      .duration(1000)
      .attr("transform", "translate(100,100) rotate(180)")
      .transition()
      .delay(1000)
      .duration(1000)
      .attr("transform", "translate(100,100) rotate(360)");
    * {
      margin: 0;
      padding: 0;
      border: 0;
    }
    
    body {
      background: #ffd;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg></svg>

    我可以破解这样一个代码,将下半个旋转分成两个四分之一顺时针旋转,但我想知道是否有更优雅的解决方案。

    2 回复  |  直到 7 年前
        1
  •  4
  •   Gerardo Furtado    7 年前

    这里的罪魁祸首是D3本身,而不是任何SVG规范。

    问题是您的转换使用了 d3.interpolateTransform 如我们所见 here :

    var fullname = namespace(name), i = fullname === "transform" ? interpolateTransform : interpolate;
    

    这是v4源代码,不是v3,但原理是一样的,正如您在实际的v3代码中看到的:

    var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
    

    interpolateTransform (同样是v4,但v3几乎相同),我们将看到它使用了一个名为 parseSvg 用于计算新变换的矩阵的:

    function parseSvg(value) {
      if (value == null) return identity;
      if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
      svgNode.setAttribute("transform", value);
      if (!(value = svgNode.transform.baseVal.consolidate())) return identity;
      value = value.matrix;
      return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
    }
    

    0 作为传递时矩阵中的最终值 rotate(360) -2.4492935982947064e-16 ,这实际上是零)。

    解决方案

    这里有几种可能的解决方案,最简单的是使用 interpolateString 而不是 插值变换

    d3.transform() ,已在v4/v5中删除:

    d3.interpolateString(d3.transform(d3.select(this).attr("transform")), "translate(100,100) rotate(360)")
    

    以下是更改后的代码:

    var svg = d3.select('svg');
    
    var s = svg.append("rect")
      .attr("width", 50)
      .attr("height", 50)
      .attr("x", -25)
      .attr("y", -25)
      .attr("fill", "red")
      .attr("transform", "translate(100,100)");
    
    s.transition()
      .duration(1000)
      .attr("transform", "translate(100,100) rotate(180)")
      .transition()
      .delay(1000)
      .duration(1000)
      .attrTween("transform", function() {
        return d3.interpolateString(d3.transform(d3.select(this).attr("transform")), "translate(100,100) rotate(360)")
      });
    <svg></svg>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
        2
  •  1
  •   rioV8    7 年前

    如果我使用D3v3,它不会旋转到180度,如果我切换到D3v4,它会旋转到180度。

    可以插值到359.99。它不跟随文档中的字符串插值器,因为您还可以 scale() 在变换中。

    translate(100, 100) rotate(359.989990234375) scale(0.999,0.999)
    

    如果编写自己的插值器,则不会发生这种情况。

    var svg = d3.select('svg');
    
    var s=svg.append("rect")
    .attr("width",50)
    .attr("height",50)
    .attr("x",-25)
    .attr("y",-25)
    .attr("fill","red")
    .attr("transform","translate(100,100) rotate(0)");
    
    s
    .transition()
    .duration(3000)
    .ease(d3.easeLinear)
    .attr("transform","translate(100,100) rotate(180)")
    .transition()
    .delay(2000)
    .duration(3000)
    .ease(d3.easeLinear)
    .attrTween("transform", () => d3.interpolate("translate(100,100) rotate(180)", "translate(100,100) rotate(360)") );
    <script src="https://d3js.org/d3.v5.min.js"></script>
    
    <svg></svg>