简体   繁体   English

D3JS:动画一个 2 圆弧甜甜圈

[英]D3JS: Animate a 2 arc donut

I'm kind of new to D3JS and I've made a 2 arc Donut (my data has only two values).我是 D3JS 的新手,我做了一个 2 弧甜甜圈(我的数据只有两个值)。 I'm trying to figure out how to animate them so that the largest path get "self drawn" first and then the smallest one gets self drawn as well.我试图弄清楚如何为它们设置动画,以便最大的路径首先“自绘”,然后最小的路径也自绘。

I know this can be achieved setting the start angle to 0 and then use the D3js attrTween() and play with the desired final angle.我知道这可以通过将起始角度设置为 0 来实现,然后使用 D3js attrTween() 并使用所需的最终角度进行播放。 but since I can not declare the final angle by myself and it depends on the data, and due to my very little knowledge about djse I haven´t been able to achieve it.但由于我不能自己声明最终角度,这取决于数据,并且由于我对 djse 的了解很少,我无法实现它。

This is my JS code:这是我的 JS 代码:

var width = 100,
  height = 100,
  radius = (Math.min(width, height) / 2);

function drawDonut(dataa, divchart) {
  var sym = "%"

  var color = ["#00338D", "#BC204B"];

  var pie = d3.pie()
    .value(function(d) {
      return d
    })(dataa);

  var arc = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(radius - (radius / 1.9));

  var labelArc = d3.arc()
    .outerRadius(radius - 31)
    .innerRadius(radius - 31);

  var svg = d3.select(divchart)
    .append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + 50 + "," + 50 + ")");

  var g = svg.selectAll("arc")
    .data(pie)
    .enter().append("g")
    .attr("class", "arc");

  g.append("path")
    .attr("d", arc)
    .data(color)
    .style("fill", function(d) {
      return d
    });

  g.append("g")
    .attr("transform", "translate(-17,-17) scale(0.7)")
    .html(myGroup);    
}

Using the data from your previous post and attrTween as a reference, here's a snippet with the large-path-first animation applied to使用您一篇文章中的数据attrTween作为参考,这是一个片段,其中应用了大路径优先动画

 var myGroup = '<g><path class="st0" d="M15.6,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C13.2,9.7,14.2,10.9,15.6,10.9L15.6,10.9z"/><path class="st0" d="M18.6,11.6h-1.2l-1.8,5.5l-1.8-5.5h-1.2c-1.3,0-2.4,1.2-2.4,2.7v13h2.4l1.2,16.4h3.6l1.2-16.4H21v-13C21,12.8,19.9,11.6,18.6,11.6L18.6,11.6z"/><path class="st0" d="M31.9,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C29.5,9.7,30.6,10.9,31.9,10.9L31.9,10.9z"/><path class="st0" d="M39.8,25.2l-3.6-11.6c0,0-0.6-2-2.4-2h-3.6c-1.8,0-2.4,2-2.4,2l-3.6,11.6l1.2,0.7l4.2-9.5l-3.6,14.3h3.6l1.2,13h2.4l1.2-13H38l-3.6-14.3l4.2,9.5L39.8,25.2L39.8,25.2z"/></g>'; var data1 = [4, 96]; var data2 = [1, 99]; var data3 = [16, 84]; var data4 = [12, 88]; var data5 = [29, 71]; var data6 = [15, 85]; var data7 = [12, 88]; var data8 = [10, 90]; var width = 100, height = 100, radius = (Math.min(width, height) / 2); function drawDonut(data, divchart) { var sym = "%" var color = ["#BC204B","#00338D"]; // sort data data = data.sort(function (a, b) { return ba; }); var pie = d3.pie() .value(function(d) { return d })(data); var arc = d3.arc() .outerRadius(radius - 10) .innerRadius(radius - (radius / 1.9)); var labelArc = d3.arc() .outerRadius(radius - 31) .innerRadius(radius - 31); var svg = d3.select(divchart) .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + 50 + "," + 50 + ")"); var g = svg.selectAll("arc") .data(pie) .enter().append("g") .attr("class", "arc"); g.append("path") .style("fill", function(d, i) { return color[i]; }) .transition().delay(function (d, i) { return i*800}).duration(800) .attrTween("d", arcTween) function arcTween(d) { var i = d3.interpolate(d.startAngle, d.endAngle); return function (t) { d.endAngle = i(t); return arc(d); } } g.append("g") .attr("transform", "translate(-17,-17) scale(0.7)") .html(myGroup); } drawDonut(data1, "#pie1") drawDonut(data2, "#pie2") drawDonut(data3, "#pie3") drawDonut(data4, "#pie4") drawDonut(data5, "#pie5") drawDonut(data6, "#pie6") drawDonut(data7, "#pie7") drawDonut(data8, "#pie8")
 div { display: inline; }
 <script src="https://d3js.org/d3.v5.min.js"></script> <div id="pie1"></div> <div id="pie2"></div> <div id="pie3"></div> <div id="pie4"></div> <div id="pie5"></div> <div id="pie6"></div> <div id="pie7"></div> <div id="pie1"></div> <div id="pie8"></div>

Relevant changes:相关变化:

  1. AttrTween function AttrTween 函数

    function arcTween(d) { var i = d3.interpolate(d.startAngle, d.endAngle); return function (t) { d.endAngle = i(t); return arc(d); } }
  2. Use the above for the paths along with a delay in the transition which btw is used to animate the paths in sequence.将上述内容用于路径以及过渡中的延迟,顺便说一句,该延迟用于按顺序为路径设置动画。

     g.append("path") .style("fill", function(d, i) { return color[i]; }) .transition().delay(function (d, i) { return i*800}).duration(800) .attrTween("d", arcTween);
  3. Sorted data in a descending order to draw the largest arc first.按降序对数据进行排序以首先绘制最大的弧。

     // sort data data = data.sort(function (a, b) { return ba; });

Based on the answer by Shashank I like to propose a modification根据Shashank的回答,我想提出修改

  • do not sort the data because the order looks important (d[0] == man, d[1] == woman) and the arcs are colored in order不要对data进行排序,因为顺序看起来很重要(d[0] == man,d[1] == woman)并且弧按顺序着色

    // sort data //data = data.sort(function (a, b) { return ba; });
  • the pie generator should also not sort the data based on value饼图生成器也不应该根据值对数据进行排序

    var pie = d3.pie() .sortValues(null) // both null means NO sort .value(function(d) { return d })(data);
  • in Shashanks answer both parts are animated with easeCubic and each part (small or large) takes 800ms.在 Shashanks 的回答中,这两个部分都用easeCubic进行了动画处理,每个部分(小或大)需要 800 毫秒。 To get a constant angular speed animation we must set the delay and duration based on the startAngle and the endAngle of the part.为了获得恒定的角速度动画,我们必须根据部件的startAngleendAngle设置延迟和持续时间。 And the transition must be set to easeLinear .并且过渡必须设置为easeLinear For the demo I have set the complete animation to 5000ms.对于演示,我将完整的动画设置为 5000 毫秒。

     var msecPerRad = 5000 / (2*Math.PI); g.append("path") .style("fill", function(d, i) { return color[i]; }) .transition() .ease(d3.easeLinear) .delay(function (d) { return d.startAngle * msecPerRad;}) .duration(function (d) { return (d.endAngle - d.startAngle) * msecPerRad;}) .attrTween("d", arcTween);

Example with Linear easing线性缓动示例

 var myGroup = '<g><path class="st0" d="M15.6,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C13.2,9.7,14.2,10.9,15.6,10.9L15.6,10.9z"/><path class="st0" d="M18.6,11.6h-1.2l-1.8,5.5l-1.8-5.5h-1.2c-1.3,0-2.4,1.2-2.4,2.7v13h2.4l1.2,16.4h3.6l1.2-16.4H21v-13C21,12.8,19.9,11.6,18.6,11.6L18.6,11.6z"/><path class="st0" d="M31.9,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C29.5,9.7,30.6,10.9,31.9,10.9L31.9,10.9z"/><path class="st0" d="M39.8,25.2l-3.6-11.6c0,0-0.6-2-2.4-2h-3.6c-1.8,0-2.4,2-2.4,2l-3.6,11.6l1.2,0.7l4.2-9.5l-3.6,14.3h3.6l1.2,13h2.4l1.2-13H38l-3.6-14.3l4.2,9.5L39.8,25.2L39.8,25.2z"/></g>'; var data1 = [50, 50]; var data2 = [70, 30]; var data3 = [16, 84]; var data4 = [12, 88]; var data5 = [29, 71]; var data6 = [15, 85]; var data7 = [12, 88]; var data8 = [10, 90]; var width = 100, height = 100, radius = (Math.min(width, height) / 2); function drawDonut(data, divchart) { var sym = "%" var color = ["#BC204B","#00338D"]; // sort data //data = data.sort(function (a, b) { return ba; }); var pie = d3.pie() .sortValues(null) // both null means NO sort .value(function(d) { return d })(data); var arc = d3.arc() .outerRadius(radius - 10) .innerRadius(radius - (radius / 1.9)); var labelArc = d3.arc() .outerRadius(radius - 31) .innerRadius(radius - 31); var svg = d3.select(divchart) .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + 50 + "," + 50 + ")"); var g = svg.selectAll("arc") .data(pie) .enter().append("g") .attr("class", "arc"); var msecPerRad = 5000 / (2*Math.PI); g.append("path") .style("fill", function(d, i) { return color[i]; }) .transition() .ease(d3.easeLinear) .delay(function (d) { return d.startAngle * msecPerRad;}) .duration(function (d) { return (d.endAngle - d.startAngle) * msecPerRad;}) .attrTween("d", arcTween); function arcTween(d) { var i = d3.interpolate(d.startAngle, d.endAngle); return function (t) { d.endAngle = i(t); return arc(d); } } g.append("g") .attr("transform", "translate(-17,-17) scale(0.7)") .html(myGroup); } drawDonut(data1, "#pie1") drawDonut(data2, "#pie2") drawDonut(data3, "#pie3") drawDonut(data4, "#pie4") drawDonut(data5, "#pie5") drawDonut(data6, "#pie6") drawDonut(data7, "#pie7") drawDonut(data8, "#pie8")
 div { display: inline; }
 <script src="https://d3js.org/d3.v5.min.js"></script> <div id="pie1"></div> <div id="pie2"></div> <div id="pie3"></div> <div id="pie4"></div> <div id="pie5"></div> <div id="pie6"></div> <div id="pie7"></div> <div id="pie1"></div> <div id="pie8"></div>

If you want the complete angle animation performed with an easeCubic it takes little more math.如果您想要使用easeCubic执行完整的角度动画,则需要更多的数学运算。 The main reason is that both arc segments are independent trasitions.主要原因是两个弧段都是独立的过渡。 This should work for more then 2 arc segments.这应该适用于超过 2 个弧段。

It only works for monotone increasing easing functions, not for things like bounce, elastic and back(in/out).它仅适用于单调递增的缓动函数,不适用于反弹、弹性和返回(输入/输出)等功能。

  function easeInverse(ease) {
    return function(e) {
      var min = 0, max = 1;
      while (max - min > 1e-3) {
        var mid = (max + min) * 0.5;
        emid = ease(mid);
        if (emid > e) { max = mid; }
        else { min = mid; }
      }
      return max;
    }
  }
  var inverseCubic = easeInverse(d3.easeCubic);
  var oneOver2Pi = 1.0 / (2*Math.PI);
  var total_msec = 5000;

  g.append("path")
    .style("fill", function(d, i) { return color[i]; })
    .transition()
    .ease(d3.easeLinear)
    .delay(function (d) { return total_msec * inverseCubic(d.startAngle * oneOver2Pi);})
    .duration(function (d) { return total_msec * (inverseCubic(d.endAngle * oneOver2Pi) - inverseCubic(d.startAngle * oneOver2Pi));})
    .attrTween("d", arcTween);

  function arcTween(d) {
    var i = d3.interpolate(inverseCubic(d.startAngle * oneOver2Pi), inverseCubic(d.endAngle * oneOver2Pi));
    return function (t) {
      d.endAngle = 2*Math.PI*d3.easeCubic(i(t));
      return arc(d);
    }
  }

Example with Cubic easing for the full circle angle.整个圆角的三次缓动示例。

 var myGroup = '<g><path class="st0" d="M15.6,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C13.2,9.7,14.2,10.9,15.6,10.9L15.6,10.9z"/><path class="st0" d="M18.6,11.6h-1.2l-1.8,5.5l-1.8-5.5h-1.2c-1.3,0-2.4,1.2-2.4,2.7v13h2.4l1.2,16.4h3.6l1.2-16.4H21v-13C21,12.8,19.9,11.6,18.6,11.6L18.6,11.6z"/><path class="st0" d="M31.9,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C29.5,9.7,30.6,10.9,31.9,10.9L31.9,10.9z"/><path class="st0" d="M39.8,25.2l-3.6-11.6c0,0-0.6-2-2.4-2h-3.6c-1.8,0-2.4,2-2.4,2l-3.6,11.6l1.2,0.7l4.2-9.5l-3.6,14.3h3.6l1.2,13h2.4l1.2-13H38l-3.6-14.3l4.2,9.5L39.8,25.2L39.8,25.2z"/></g>'; var data1 = [50, 50]; var data2 = [70, 30]; var data3 = [16, 84]; var data4 = [12, 88]; var data5 = [29, 71]; var data6 = [15, 85]; var data7 = [12, 88]; var data8 = [10, 90]; var width = 100, height = 100, radius = (Math.min(width, height) / 2); function drawDonut(data, divchart) { var sym = "%" var color = ["#BC204B","#00338D"]; // sort data //data = data.sort(function (a, b) { return ba; }); var pie = d3.pie() .sortValues(null) // both null means NO sort .value(function(d) { return d })(data); var arc = d3.arc() .outerRadius(radius - 10) .innerRadius(radius - (radius / 1.9)); var labelArc = d3.arc() .outerRadius(radius - 31) .innerRadius(radius - 31); var svg = d3.select(divchart) .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + 50 + "," + 50 + ")"); var g = svg.selectAll("arc") .data(pie) .enter().append("g") .attr("class", "arc"); function easeInverse(ease) { return function(e) { var min = 0, max = 1; while (max - min > 1e-3) { var mid = (max + min) * 0.5; emid = ease(mid); if (emid > e) { max = mid; } else { min = mid; } } return max; } } var inverseCubic = easeInverse(d3.easeCubic); var oneOver2Pi = 1.0 / (2*Math.PI); var total_msec = 5000; g.append("path") .style("fill", function(d, i) { return color[i]; }) .transition() .ease(d3.easeLinear) .delay(function (d) { return total_msec * inverseCubic(d.startAngle * oneOver2Pi);}) .duration(function (d) { return total_msec * (inverseCubic(d.endAngle * oneOver2Pi) - inverseCubic(d.startAngle * oneOver2Pi));}) .attrTween("d", arcTween); function arcTween(d) { var i = d3.interpolate(inverseCubic(d.startAngle * oneOver2Pi), inverseCubic(d.endAngle * oneOver2Pi)); return function (t) { d.endAngle = 2*Math.PI*d3.easeCubic(i(t)); return arc(d); } } g.append("g") .attr("transform", "translate(-17,-17) scale(0.7)") .html(myGroup); } drawDonut(data1, "#pie1") drawDonut(data2, "#pie2") drawDonut(data3, "#pie3") drawDonut(data4, "#pie4") drawDonut(data5, "#pie5") drawDonut(data6, "#pie6") drawDonut(data7, "#pie7") drawDonut(data8, "#pie8")
 div { display: inline; }
 <script src="https://d3js.org/d3.v5.min.js"></script> <div id="pie1"></div> <div id="pie2"></div> <div id="pie3"></div> <div id="pie4"></div> <div id="pie5"></div> <div id="pie6"></div> <div id="pie7"></div> <div id="pie1"></div> <div id="pie8"></div>

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

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