简体   繁体   English

d3弧形过渡是逆时针而非顺时针

[英]d3 arc transition anticlockwise not clockwise

I am looking at having two animated donut graphs and need to animate them a certain way. 我正在查看有两个动画甜甜圈图,需要以某种方式对其进行动画处理。

If you look at the jsfiddle: 如果您看一下jsfiddle:

https://jsfiddle.net/5uc1xfxm/ https://jsfiddle.net/5uc1xfxm/

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <meta name="robots" content="noindex, nofollow">
  <meta name="googlebot" content="noindex, nofollow">

<script type="text/javascript" src="/js/lib/dummy.js"></script>

<link rel="stylesheet" type="text/css" href="/css/result-light.css">

<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>

    <style type="text/css">
    .animated-ring {
  margin-left: auto;
  margin-right: auto;
  //margin-top: 50px;
  width: 200px;
  background-color: #fff;
}
  </style>

<script type='text/javascript'>//<![CDATA[
window.onload=function(){
var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto

// An arc function with all values bound except the endAngle. So, to compute an
// SVG path string for a given angle, we pass an object with an endAngle
// property to the `arc` function, and it will return the corresponding string.
var arc1 = d3.arc()
  .innerRadius(45)
  .outerRadius(90)
  .startAngle(0.75 * tau);

var arc2 = d3.arc()
  .innerRadius(45)
  .outerRadius(90)
  .startAngle(0.25 * tau);

// Get the SVG container, and apply a transform such that the origin is the
// center of the canvas. This way, we don’t need to position arcs individually.
var svg1 = d3.select("#anim1"),
  width = +svg1.attr("width"),
  height = +svg1.attr("height"),
  g = svg1.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

var svg2 = d3.select("#anim2"),
  width = +svg2.attr("width"),
  height = +svg2.attr("height"),
  h = svg2.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// Add the background arc, from 0 to 100% (tau).
var background1 = g.append("path")
  .datum({
    endAngle: tau
  })
  .style("fill", "transparent")
  .attr("d", arc1);

var background2 = h.append("path")
  .datum({
    endAngle: tau
  })
  .style("fill", "transparent")
  .attr("d", arc2);

// Add the foreground arc in orange, currently showing 12.7%.
var foreground1 = g.append("path")
  .datum({
    endAngle: 0.75 * tau
  })
  .style("fill", "#EF4939")
  .attr("d", arc1);

var foreground2 = h.append("path")
  .datum({
    endAngle: 0.25 * tau
  })
  .style("fill", "blue")
  .attr("d", arc2);

// Every so often, start a transition to a new random angle. The attrTween
// definition is encapsulated in a separate function (a closure) below.
d3.timer(function() {
  foreground1.transition()
    .duration(75)
    .attrTween("d", arcTween(0 * tau));
  foreground2.transition()
    .duration(75)
    .attrTween("d", arcTween2(0.5 * tau));
});

// Returns a tween for a transition’s "d" attribute, transitioning any selected
// arcs from their current angle to the specified new angle.
function arcTween(newAngle) {

  // The function passed to attrTween is invoked for each selected element when
  // the transition starts, and for each element returns the interpolator to use
  // over the course of transition. This function is thus responsible for
  // determining the starting angle of the transition (which is pulled from the
  // element’s bound datum, d.endAngle), and the ending angle (simply the
  // newAngle argument to the enclosing function).
  return function(d) {

    // To interpolate between the two angles, we use the default d3.interpolate.
    // (Internally, this maps to d3.interpolateNumber, since both of the
    // arguments to d3.interpolate are numbers.) The returned function takes a
    // single argument t and returns a number between the starting angle and the
    // ending angle. When t = 0, it returns d.endAngle; when t = 1, it returns
    // newAngle; and for 0 < t < 1 it returns an angle in-between.
    var interpolate = d3.interpolate(d.endAngle, newAngle);

    // The return value of the attrTween is also a function: the function that
    // we want to run for each tick of the transition. Because we used
    // attrTween("d"), the return value of this last function will be set to the
    // "d" attribute at every tick. (It’s also possible to use transition.tween
    // to run arbitrary code for every tick, say if you want to set multiple
    // attributes from a single function.) The argument t ranges from 0, at the
    // start of the transition, to 1, at the end.
    return function(t) {

      // Calculate the current arc angle based on the transition time, t. Since
      // the t for the transition and the t for the interpolate both range from
      // 0 to 1, we can pass t directly to the interpolator.
      //
      // Note that the interpolated angle is written into the element’s bound
      // data object! This is important: it means that if the transition were
      // interrupted, the data bound to the element would still be consistent
      // with its appearance. Whenever we start a new arc transition, the
      // correct starting angle can be inferred from the data.
      d.endAngle = interpolate(t);

      // Lastly, compute the arc path given the updated data! In effect, this
      // transition uses data-space interpolation: the data is interpolated
      // (that is, the end angle) rather than the path string itself.
      // Interpolating the angles in polar coordinates, rather than the raw path
      // string, produces valid intermediate arcs during the transition.
      return arc1(d);
    };
  };
}

// Returns a tween for a transition’s "d" attribute, transitioning any selected
// arcs from their current angle to the specified new angle.
function arcTween2(newAngle) {
  return function(d) {
    var interpolate = d3.interpolate(d.endAngle, newAngle);
    return function(t) {
            d.endAngle = interpolate(t);
      return arc2(d);
    };
  };
}
}//]]> 

</script>


</head>

<body>
  <div class="animated-ring">
  <svg width="200" height="200" id="anim1"></svg>
  <svg width="200" height="200" id="anim2"></svg>
</div>

  <script>
  // tell the embed parent frame the height of the content
  if (window.parent && window.parent.parent){
    window.parent.parent.postMessage(["resultsFrame", {
      height: document.body.getBoundingClientRect().height,
      slug: "5uc1xfxm"
    }], "*")
  }
</script>

</body>

</html>

You will see the two animations. 您将看到两个动画。 The orange graph is working as required but the blue graph isn't. 橙色图可以按要求工作,而蓝色图则不能。

The start and end positions are correct but instead of it moving clockwise and filling only a quarter of the circle. 起点和终点位置是正确的,但不是顺时针移动并且仅填充了四分之一圆。 I need it to move anti-clockwise and fill three quarters of the circle. 我需要它逆时针移动并填充四分之三的圆。

Any ideas? 有任何想法吗?

Assuming I understand the problem, 假设我了解问题所在,

Instead of having the arc transition from 0.25 tau to 0.5 tau, try -0.5 tau: 而不是使弧度从0.25 tau过渡到0.5 tau,请尝试-0.5 tau:

.attrTween("d", arcTween2(-0.5 * tau));

This will move the arc backwards and cause it to fill 3/4 of the complete donut. 这将使弧向后移动,并使其充满整个甜甜圈的3/4。 This fits the pattern from the first donut which behaves properly, where the ultimate end angle is less than the start angle. 这适合表现良好的第一个甜甜圈的图案,其最终终止角度小于起始角度。

Updated fiddle here 在这里更新小提琴

And a snippet using your code for good measure (I've changed the order of the donuts so you don't need to quickly scroll): 和一小段使用您的代码的代码(我已更改了甜甜圈的顺序,因此您无需快速滚动):

 <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="robots" content="noindex, nofollow"> <meta name="googlebot" content="noindex, nofollow"> <script type="text/javascript" src="/js/lib/dummy.js"></script> <link rel="stylesheet" type="text/css" href="/css/result-light.css"> <script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script> <style type="text/css"> .animated-ring { margin-left: auto; margin-right: auto; //margin-top: 50px; width: 200px; background-color: #fff; } </style> <script type='text/javascript'>//<![CDATA[ window.onload=function(){ var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto // An arc function with all values bound except the endAngle. So, to compute an // SVG path string for a given angle, we pass an object with an endAngle // property to the `arc` function, and it will return the corresponding string. var arc1 = d3.arc() .innerRadius(45) .outerRadius(90) .startAngle(0.75 * tau); var arc2 = d3.arc() .innerRadius(45) .outerRadius(90) .startAngle(0.25 * tau); // Get the SVG container, and apply a transform such that the origin is the // center of the canvas. This way, we don't need to position arcs individually. var svg1 = d3.select("#anim1"), width = +svg1.attr("width"), height = +svg1.attr("height"), g = svg1.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var svg2 = d3.select("#anim2"), width = +svg2.attr("width"), height = +svg2.attr("height"), h = svg2.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); // Add the background arc, from 0 to 100% (tau). var background1 = g.append("path") .datum({ endAngle: tau }) .style("fill", "transparent") .attr("d", arc1); var background2 = h.append("path") .datum({ endAngle: tau }) .style("fill", "transparent") .attr("d", arc2); // Add the foreground arc in orange, currently showing 12.7%. var foreground1 = g.append("path") .datum({ endAngle: 0.75 * tau }) .style("fill", "#EF4939") .attr("d", arc1); var foreground2 = h.append("path") .datum({ endAngle: 0.25 * tau }) .style("fill", "blue") .attr("d", arc2); // Every so often, start a transition to a new random angle. The attrTween // definition is encapsulated in a separate function (a closure) below. d3.timer(function() { foreground1.transition() .duration(75) .attrTween("d", arcTween(0 * tau)); foreground2.transition() .duration(75) .attrTween("d", arcTween2(-0.5 * tau)); }); // Returns a tween for a transition's "d" attribute, transitioning any selected // arcs from their current angle to the specified new angle. function arcTween(newAngle) { // The function passed to attrTween is invoked for each selected element when // the transition starts, and for each element returns the interpolator to use // over the course of transition. This function is thus responsible for // determining the starting angle of the transition (which is pulled from the // element's bound datum, d.endAngle), and the ending angle (simply the // newAngle argument to the enclosing function). return function(d) { // To interpolate between the two angles, we use the default d3.interpolate. // (Internally, this maps to d3.interpolateNumber, since both of the // arguments to d3.interpolate are numbers.) The returned function takes a // single argument t and returns a number between the starting angle and the // ending angle. When t = 0, it returns d.endAngle; when t = 1, it returns // newAngle; and for 0 < t < 1 it returns an angle in-between. var interpolate = d3.interpolate(d.endAngle, newAngle); // The return value of the attrTween is also a function: the function that // we want to run for each tick of the transition. Because we used // attrTween("d"), the return value of this last function will be set to the // "d" attribute at every tick. (It's also possible to use transition.tween // to run arbitrary code for every tick, say if you want to set multiple // attributes from a single function.) The argument t ranges from 0, at the // start of the transition, to 1, at the end. return function(t) { // Calculate the current arc angle based on the transition time, t. Since // the t for the transition and the t for the interpolate both range from // 0 to 1, we can pass t directly to the interpolator. // // Note that the interpolated angle is written into the element's bound // data object! This is important: it means that if the transition were // interrupted, the data bound to the element would still be consistent // with its appearance. Whenever we start a new arc transition, the // correct starting angle can be inferred from the data. d.endAngle = interpolate(t); // Lastly, compute the arc path given the updated data! In effect, this // transition uses data-space interpolation: the data is interpolated // (that is, the end angle) rather than the path string itself. // Interpolating the angles in polar coordinates, rather than the raw path // string, produces valid intermediate arcs during the transition. return arc1(d); }; }; } // Returns a tween for a transition's "d" attribute, transitioning any selected // arcs from their current angle to the specified new angle. function arcTween2(newAngle) { return function(d) { var interpolate = d3.interpolate(d.endAngle, newAngle); return function(t) { d.endAngle = interpolate(t); return arc2(d); }; }; } }//]]> </script> </head> <body> <div class="animated-ring"> <svg width="200" height="200" id="anim2"></svg> <svg width="200" height="200" id="anim1"></svg> </div> <script> // tell the embed parent frame the height of the content if (window.parent && window.parent.parent){ window.parent.parent.postMessage(["resultsFrame", { height: document.body.getBoundingClientRect().height, slug: "5uc1xfxm" }], "*") } </script> </body> </html> 

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

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