首页 > 解决方案 > 饼图插值无法正常工作

问题描述

以下是我正在处理的饼图:http: //bl.ocks.org/lydiawawa/9fb92d9982f0b41b63be142ce279acf5/52e3b54ea61fc55071992d15993a5fbe21174729

它应该是一个非常简单的代码来学习,但是在我将代码更新到 D3 v4 后,当我从 Apple 切换到 Orange 然后 Apple 时,缺少数据并导致苹果饼图出现间隙。

这部分代码可能有问题,我不知道这仅在 D3 v3 中有效,但在 V4 中无效:

// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
  var i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
  return arc(i(t));
  };
}
// Interpolate exiting arcs start and end angles to Math.PI * 2
// so that they 'exit' at the end of the data
function arcTweenOut(a) {
  var i = d3.interpolate(this._current, {startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0});
  this._current = i(0);
  return function (t) {
    return arc(i(t));
  };
}

标签: javascriptd3.js

解决方案


在 D3 v4/5 中,输入选择不再修改更新选择。您必须相应地更改更新选择或合并选择。

我以有目的地详细的方式重写了您的选择,命名了所有选择,因此您可以看到发生了什么:

var paths = svg.selectAll("path").data(pie(dataset[this.value]));

var pathsExit = paths.exit()
    .transition()
    .duration(750)
    .attrTween('d', arcTweenOut)
    .remove();

var pathsEnter = paths.enter().append("path")
    .attr("fill", function(d, i) {
        return color(i);
    })
    .attr("d", arc(enterAntiClockwise))
    .each(function(d) {
        this._current = {
            data: d.data,
            value: d.value,
            startAngle: enterAntiClockwise.startAngle,
            endAngle: enterAntiClockwise.endAngle
        };
    });

paths = pathsEnter.merge(paths);

paths.transition().duration(750).attrTween("d", arcTween);

我还创建了第 5 种颜色,这是必要的(除非你想要重复)。

这是您更新的代码:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  body {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin: auto;
    position: relative;
    width: 960px;
  }

  text {
    font: 10px sans-serif;
  }

  form {
    position: absolute;
    right: 10px;
    top: 10px;
  }

</style>
<form>
  <label><input type="radio" name="dataset" value="apples" checked> Apples</label>
  <label><input type="radio" name="dataset" value="oranges"> Oranges</label>
</form>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  var dataset = {
    apples: [53245, 28479, 19697, 24037, 40245],
    oranges: [200, 200, 200, 200] // previously 5 values, now only 4
  };

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

  var enterAntiClockwise = {
    startAngle: Math.PI * 2,
    endAngle: Math.PI * 2
  };

  var color = d3.scaleOrdinal()
    .domain(d3.range(0, length))
    .range(["#9E519F", "#ADBCCC", "#0079BB", "#6d7fcc", "teal"])

  var pie = d3.pie()
    .sort(null);

  var arc = d3.arc()
    .innerRadius(radius - 100)
    .outerRadius(radius - 20);

  var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

  var path = svg.selectAll("path")
    .data(pie(dataset.apples))
    .enter().append("path")
    .attr("fill", function(d, i) {
      return color(i);
    })
    .attr("d", arc)
    .each(function(d) {
      this._current = d;
    }); // store the initial values

  d3.selectAll("input").on("change", change);

  var timeout = setTimeout(function() {
    d3.select("input[value=\"oranges\"]").property("checked", true).each(change);
  }, 2000);

  function change() {
    clearTimeout(timeout);
    var paths = svg.selectAll("path").data(pie(dataset[this.value])); // update the data
    // set the start and end angles to Math.PI * 2 so we can transition
    var pathsExit = paths.exit()
      .transition()
      .duration(750)
      .attrTween('d', arcTweenOut)
      .remove() // now remove the exiting arcs
    // anticlockwise to the actual values later
    var pathsEnter = paths.enter().append("path")
      .attr("fill", function(d, i) {
        return color(i);
      })
      .attr("d", arc(enterAntiClockwise))
      .each(function(d) {
        this._current = {
          data: d.data,
          value: d.value,
          startAngle: enterAntiClockwise.startAngle,
          endAngle: enterAntiClockwise.endAngle
        };
      });

    paths = pathsEnter.merge(paths);

    paths.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
  }

  // Store the displayed angles in _current.
  // Then, interpolate from _current to the new angles.
  // During the transition, _current is updated in-place by d3.interpolate.
  function arcTween(a) {
    var i = d3.interpolate(this._current, a);
    this._current = i(0);
    return function(t) {
      return arc(i(t));
    };
  }
  // Interpolate exiting arcs start and end angles to Math.PI * 2
  // so that they 'exit' at the end of the data
  function arcTweenOut(a) {
    var i = d3.interpolate(this._current, {
      startAngle: Math.PI * 2,
      endAngle: Math.PI * 2,
      value: 0
    });
    this._current = i(0);
    return function(t) {
      return arc(i(t));
    };
  }

</script>


推荐阅读