【问题标题】:how to obtain a complete clockwise rotation in D3如何在 D3 中获得完整的顺时针旋转
【发布时间】:2019-05-28 15:14:53
【问题描述】:

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

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>

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

【问题讨论】:

    标签: javascript d3.js svg rotation transition


    【解决方案1】:

    这里的罪魁祸首是 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);
    }
    

    当您将rotate(360) 传递给该函数时,该函数将生成0 作为矩阵中的最终值(实际值为-2.4492935982947064e-16,实际上为零)。

    解决方案

    这里有几种可能的解决方案,最简单的一种是使用interpolateString 而不是interpolateTransform

    此外,由于您的代码使用 D3 v3,您可以利用在 v4/v5 中删除的 d3.transform()

    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】:

      如果我使用 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>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-19
        • 1970-01-01
        • 2012-05-16
        • 2012-05-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多