D3JS:对2弧形甜甜圈进行动画处理

时间:2018-08-20 14:01:36

标签: javascript animation d3.js

我是D3JS的新手,我做了一个2弧形的Doughnut(我的数据只有两个值)。我试图弄清楚如何为它们设置动画,以便最大的路径首先获得“自绘”,然后最小的路径也获得自绘。我知道可以将起始角度设置为0,然后使用D3js attrTween()并以所需的最终角度进行播放。但是由于我自己不能声明最终角度,并且它取决于数据,并且由于我对djse的了解很少,所以我无法实现它。谁能帮我解决这个问题?非常感谢。

这是我的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);    
}

非常感谢您能带给我的帮助。

2 个答案:

答案 0 :(得分:2)

使用previous postattrTween中的数据作为参考,这是一个片段,其中将大路径优先动画应用于

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 b-a; });
  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>

相关更改:

  1. AttrTween函数

    function arcTween(d) {
      var i = d3.interpolate(d.startAngle, d.endAngle);
      return function (t) {
        d.endAngle = i(t);
        return arc(d);
      }
    }
    
  2. 将以上内容用于路径以及过渡中的延迟(btw用于按顺序对路径进行动画处理。)

    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. 以降序排列的数据先绘制最大弧。

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

答案 1 :(得分:1)

根据Shashank的回答,我想提出一个修改

  • 不要对data进行排序,因为顺序看起来很重要(d [0] ==男人,d [1] ==女人)并且圆弧按顺序着色

    // sort data
    //data = data.sort(function (a, b) { return b-a; });
    
  • 饼图生成器也不应基于值对数据进行排序

    var pie = d3.pie()
      .sortValues(null) // both null means NO sort
      .value(function(d) {
        return d
      })(data);
    
  • 在Shashanks中回答,两个部分都用easeCubic进行了动画处理,每个部分(小或大)都需要800毫秒。为了获得恒定的角速度动画,我们必须根据零件的startAngleendAngle设置延迟和持续时间。并且过渡必须设置为easeLinear。对于演示,我将完整的动画设置为5000ms。

    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);
    

线性宽松示例

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 b-a; });
  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>

如果您希望使用easeCubic进行完整的角度动画处理,则只需一点数学即可。主要原因是两个弧段都是独立的过渡。这应该适用于超过2个弧段。

它仅适用于单调增大缓动功能,不适用于弹跳,弹性和后退(进/出)之类的功能。

  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);
    }
  }

在整个圆角处使用三次缓动的示例。

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 b-a; });
  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>