在D3.js中如何同时添加/删除数据点?

时间:2018-03-25 16:22:32

标签: d3.js data-binding transition

在下面的示例中,如果我的新数据集有新条目并且还删除了一些记录(添加了burrito,删除了apple),我如何能够同时反映这些更改?到目前为止,代码推入了一个新的栏,但没有删除第一个栏,即使我尝试重新设置x轴域,轴也不会更新。

  

块引用

<!DOCTYPE html>
<html lang="en">
    <head>
      <script src="https://d3js.org/d3.v4.min.js"></script>
        <meta charset="utf-8">
        <title>D3: Loading data from a CSV file</title>
  </head>
    <body>
    <p>Click on this text to update the chart with new data values (once).</p>
        <script type="text/javascript">

      var margin = {top: 20, right: 20, bottom: 30, left: 40},
            w = 600 - margin.left - margin.right,
            h = 300 - margin.top - margin.bottom;
      var padding = 40;

      var data = [
        { "Food": "Apples", "Deliciousness": 9, "new":4 },
        { "Food": "Green Beans", "Deliciousness": 5, "new":4 },
        { "Food": "Egg Salad Sandwich", "Deliciousness": 4, "new":4 },
        { "Food": "Cookies", "Deliciousness": 10, "new":4 },
        { "Food": "Liver", "Deliciousness": 2, "new":4 },
      ];

      data.forEach(function(d) {
        d.Deliciousness = +d.Deliciousness;
      });
            //define key
      var key = function(d) {
        return d.Food;
      }

      var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w + margin.left + margin.right + padding)
                        .attr("height", h + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left+"," +
                 margin.top+")");

      //initial state
      //scale and axis
      var xScale = d3.scaleBand()
        .domain(d=>d.Food)
        .range([0,w])
        .paddingInner(0.2);
     xScale.domain(data.map(function(d) { return d.Food; }));

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(data, d=>d.Deliciousness)])
        .rangeRound([h,0]);


      var xAxis = d3.axisBottom()
        .scale(xScale)
        .ticks(5);

      var yAxis = d3.axisLeft()
        .scale(yScale)
        .ticks(5);

      //draw rect
      svg.selectAll('rect')
        .data(data, key)
        .enter()
        .append('rect')
        .attr('x',(d,i) => margin.left + i * ((w + 20 ) / data.length))
        .attr('y',d=>yScale(d.Deliciousness))
        .attr('width', xScale.bandwidth())
        .attr('height',d =>h-yScale(d.Deliciousness))
        .attr('fill',function(d){
          if (d===30) return "red";
          return "rgb(0,0,"+d.Deliciousness*10+")" ;});

      //text label
      svg.selectAll("text")
        .data(data)
        .enter()
        .append("text")
        .text(d=>d.Deliciousness)
        .attr('x',(d,i) => margin.left + i * ((w + 20 ) / data.length) + 0.4*w/ data.length)
        .attr("y", d=>yScale(d.Deliciousness)+15)
        .attr("fill","white")
        .attr("text-anchor", "middle");

      //draw axis
      svg.append("g")
        .attr("class", "axis")
        .attr("transform", "translate(" + padding + ",0)")
        .call(yAxis);

      svg.append("g")
        .attr("class", "axis")
        .attr("transform", "translate(" + margin.left + "," + h + ")")
        .call(xAxis);

      //transition
      d3.select("p")
                .on("click", function() {

                    //New values for dataset
      data = [
        { "Food": "Green Beans", "Deliciousness": 5, "new":4 },
        { "Food": "Egg Salad Sandwich", "Deliciousness": 4, "new":4 },
        { "Food": "Cookies", "Deliciousness": 10, "new":4 },
        { "Food": "Liver", "Deliciousness": 2, "new":4 },
        { "Food": "Burrito", "Deliciousness": 7, "new":4 }];

          xScale.domain(data.map(function(d) { return d.Food; }));

        //Update all rects
          var bars = svg.selectAll("rect")
                       .data(data, key);

          bars.enter()
            .append("rect")
                            // <-- This makes it a smooth transition!
            .attr("x", w)

            .attr('y',d=>yScale(d.Deliciousness))
            .attr("width", xScale.bandwidth())
            .attr('height',d =>h-yScale(d.Deliciousness))
            .merge(bars)                            //Merges the enter selection with the update selection
                        .transition()                           //Initiate a transition on all elements in the update selection (all rects)
                        .duration(500)
            .attr('x',(d,i) => margin.left + i * ((w + 20 ) / data.length))
                .attr('y',d=>yScale(d.Deliciousness))
                .attr('width', xScale.bandwidth())
                .attr('height',d =>h-yScale(d.Deliciousness))
                });





        </script>
    </body>
</html>

附加了当前错误的输出。 enter image description here 谢谢!

1 个答案:

答案 0 :(得分:2)

你错过了两件事。一,.remove出口选择和两个,更新你的x轴。

xScale.domain(data.map(function(d) {
  return d.Food;
}));

// redraw x-axis
svg.select('.xaxis')
  .call(xAxis);

//Update all rects
var bars = svg.selectAll("rect")
  .data(data, key);

// remove those things exitiing
bars.exit().remove();

运行代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <meta charset="utf-8">
  <title>D3: Loading data from a CSV file</title>
</head>

<body>
  <p>Click on this text to update the chart with new data values (once).</p>
  <script type="text/javascript">
    var margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 40
      },
      w = 600 - margin.left - margin.right,
      h = 300 - margin.top - margin.bottom;
    var padding = 40;

    var data = [{
      "Food": "Apples",
      "Deliciousness": 9,
      "new": 4
    }, {
      "Food": "Green Beans",
      "Deliciousness": 5,
      "new": 4
    }, {
      "Food": "Egg Salad Sandwich",
      "Deliciousness": 4,
      "new": 4
    }, {
      "Food": "Cookies",
      "Deliciousness": 10,
      "new": 4
    }, {
      "Food": "Liver",
      "Deliciousness": 2,
      "new": 4
    }, ];

    data.forEach(function(d) {
      d.Deliciousness = +d.Deliciousness;
    });
    //define key
    var key = function(d) {
      return d.Food;
    }

    var svg = d3.select("body")
      .append("svg")
      .attr("width", w + margin.left + margin.right + padding)
      .attr("height", h + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," +
        margin.top + ")");

    //initial state
    //scale and axis
    var xScale = d3.scaleBand()
      .domain(d => d.Food)
      .range([0, w])
      .paddingInner(0.2);

    xScale.domain(data.map(function(d) {
      return d.Food;
    }));

    var yScale = d3.scaleLinear()
      .domain([0, d3.max(data, d => d.Deliciousness)])
      .rangeRound([h, 0]);


    var xAxis = d3.axisBottom()
      .scale(xScale)
      .ticks(5);

    var yAxis = d3.axisLeft()
      .scale(yScale)
      .ticks(5);

    //draw rect
    svg.selectAll('rect')
      .data(data, key)
      .enter()
      .append('rect')
      .attr('x', (d, i) => margin.left + i * ((w + 20) / data.length))
      .attr('y', d => yScale(d.Deliciousness))
      .attr('width', xScale.bandwidth())
      .attr('height', d => h - yScale(d.Deliciousness))
      .attr('fill', function(d) {
        if (d === 30) return "red";
        return "rgb(0,0," + d.Deliciousness * 10 + ")";
      });

    //text label
    svg.selectAll("text")
      .data(data)
      .enter()
      .append("text")
      .text(d => d.Deliciousness)
      .attr('x', (d, i) => margin.left + i * ((w + 20) / data.length) + 0.4 * w / data.length)
      .attr("y", d => yScale(d.Deliciousness) + 15)
      .attr("fill", "white")
      .attr("text-anchor", "middle");

    //draw axis
    svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(" + padding + ",0)")
      .call(yAxis);

    svg.append("g")
      .attr("class", "xaxis")
      .attr("transform", "translate(" + margin.left + "," + h + ")")
      .call(xAxis);

    //transition
    d3.select("p")
      .on("click", function() {

        //New values for dataset
        data = [{
          "Food": "Green Beans",
          "Deliciousness": 5,
          "new": 4
        }, {
          "Food": "Egg Salad Sandwich",
          "Deliciousness": 4,
          "new": 4
        }, {
          "Food": "Cookies",
          "Deliciousness": 10,
          "new": 4
        }, {
          "Food": "Liver",
          "Deliciousness": 2,
          "new": 4
        }, {
          "Food": "Burrito",
          "Deliciousness": 7,
          "new": 4
        }];

        xScale.domain(data.map(function(d) {
          return d.Food;
        }));
        
        svg.select('.xaxis')
          .call(xAxis);

        //Update all rects
        var bars = svg.selectAll("rect")
          .data(data, key);

        bars.exit().remove();

        bars.enter()
          .append("rect")
          // <-- This makes it a smooth transition!
          .attr("x", w)

        .attr('y', d => yScale(d.Deliciousness))
          .attr("width", xScale.bandwidth())
          .attr('height', d => h - yScale(d.Deliciousness))
          .merge(bars) //Merges the enter selection with the update selection
          .transition() //Initiate a transition on all elements in the update selection (all rects)
          .duration(500)
          .attr('x', (d, i) => margin.left + i * ((w + 20) / data.length))
          .attr('y', d => yScale(d.Deliciousness))
          .attr('width', xScale.bandwidth())
          .attr('height', d => h - yScale(d.Deliciousness));


      });
  </script>
</body>

</html>