简体   繁体   English

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

[英]how to obtain a complete clockwise rotation in D3

I want a square to complete a full clockwise rotation on itself, after a pause on the half of the rotation. 在旋转一半的暂停之后,我想要一个正方形来完成顺时针旋转。 The following code makes it doing an half rotation clockwise, and the other half counter-clockwise, contrary to what I expect. 下面的代码使它顺时针旋转半圈,另一半逆时针旋转,与我的预期相反。

 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> 

I can hack such a code splitting the second half rotation in two quarter clockwise rotations, but I wish to know if there is a more elegant solution. 我可以破解这样一个代码,分成两个四分之一顺时针旋转的后半旋转,但我想知道是否有更优雅的解决方案。

The culprit here is D3 itself, not any SVG spec. 这里的罪魁祸首是D3本身,而不是任何SVG规范。

The problem is that your transition uses d3.interpolateTransform , as we can see here : 问题是您的转换使用了d3.interpolateTransform ,我们可以在这里看到:

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

This is v4 source code, not v3, but the principle is the same, as you can see in the actual v3 code: 这是v4源代码,而不是v3,但原理是相同的,正如您在实际的v3代码中看到的那样:

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

Then, if we look in the source code for interpolateTransform (again, v4, but v3 is almost the same), we'll see that it uses a function called parseSvg that calculates the matrix for the new transform: 然后,如果我们查看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);
}

That function is generating 0 as the final value in the matrix when you pass rotate(360) to it (the actual value is -2.4492935982947064e-16 , which is practically zero). 当您将rotate(360)传递给矩阵时,该函数将生成0作为矩阵中的最终值(实际值为-2.4492935982947064e-16 ,实际上为零)。

Solution

There are several possible solutions here, the easiest one is using interpolateString instead of interpolateTransform . 这里有几种可能的解决方案,最简单的解决方案是使用interpolateString而不是interpolateTransform

Also, since your code uses D3 v3, you can take advantage of d3.transform() , which was removed in v4/v5: 此外,由于您的代码使用D3 v3,您可以利用在v4 / v5中删除的d3.transform()

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

Here is your code with that change: 以下是您更改的代码:

 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> 

If I use D3v3 it does not rotate to 180 degrees, if I switch to D3v4 it rotates to 180. 如果我使用D3v3它不会旋转到180度,如果我切换到D3v4它旋转到180度。

You can interpolate to 359.99. 你可以插值到359.99。 It does not follow the string interpolator from the docs, because you also get scale() in the transform. 它不遵循文档中的字符串插值器,因为您还在变换中获得了scale()

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

This does not happen if you write your own interpolator. 如果您编写自己的插补器,则不会发生这种情况。

 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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM