使用.update,.enter和.exit更新条形图?

时间:2016-11-04 21:26:32

标签: d3.js

我制作了一个简单的条形图。我想通过在我的数据的不同子集之间切换来更新条形图(动态)。从我所看到的,似乎有两种方法:

  1. 绘制图表,然后在按钮上单击(或其他)删除整个图表并重新绘制。
  2. 点击按钮,使用不同的数据选项“更新”,“输入”和“退出”图表中的栏。
  3. 我正在尝试选项2.这是我的代码:

    // set dimensions
    var margin = {top: 10, right: 20, bottom: 50, left: 100};
    var height = 600 -margin.top - margin.bottom;
    var width = 600 - margin.left - margin.right;
    
    // set ranges
    var x = d3.scale.linear().range([width, 0]);
    var y = d3.scale.ordinal().rangeRoundBands([0, height], .05);
    
    // define y axis
    var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");
    
    // add svg container
    var svg = d3.select("#chart")
    .append("svg")
    .attr("height", height + margin.top + margin.bottom)
    .attr("width", width + margin.left + margin.right)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    // load data
    d3.json("data.json", function(data) {
    
      // format data
      data.forEach(function(d) {
        d.enrols = +d.enrols;
      });
    
      // register click event
      d3.selectAll(".toggle").on("click", function() {
        render(this.id);
      });
    
      // initialise
      render("group-1");
    
      function render(selection) {
    
        // filter data
        var subset = data.filter(function(d) { return d.group === selection} );
    
        // scale range of data
        x.domain([0, d3.max(subset, function(d) { return d.enrols; })]);
        y.domain(subset.map(function(d) { return d.month; }));
    
        // update selection
        var bars = svg.selectAll(".bars")
        .data(subset, function(d) { return d.month; });
    
        // add bars
        bars.enter()
        .append("g")
        .attr("class", "bar")
        .attr("transform", function (d, i) {
          return "translate(0," + y(d.month) + ")";
        });
    
        bars.append("rect")
        .attr("height", y.rangeBand())
        .attr("width", function (d) {
          return width - x(d.enrols);
        });
    
        bars.append("text")
        .attr("x", function (d) {
          return width - x(d.enrols) - 15;
        })
        .attr("y", y.rangeBand() / 2)
        .attr("dy", ".35em")
        .text(function (d) {
          return d.enrols;
        });
    
        bars.exit().remove();
    
        // add y axis
        svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);
    
      }
    
    });
    

    问题是我的旧数据永远不会被退出,新条形图会被追加,但旧条形图仍然存在。我已经阅读了d3选择的理论,但我发现它们很难在实践中实现。

    这是codepen。这是data.json

2 个答案:

答案 0 :(得分:3)

非常好的是你选择了第一个选项:删除所有元素并再次绘制图表是一种非常懒惰的编码方式。

但是,在您的情况下,由于数据的长度没有变化,您不需要"输入","更新"和"退出"选择!

如果您在render函数之外移动一些不变的部分,一切都会更容易:

y.domain(data.map(function(d) {
    return d.month;
}));

var bars = svg.selectAll(".bars")
    .data(data, function(d) {
        return d.month;
    }).enter()
    .append("g")
    .attr("class", "bar")
    .attr("transform", function(d, i) {
        return "translate(0," + y(d.month) + ")";
    });

var rects = bars.append("rect")
    .attr("height", y.rangeBand())
    .attr("width", function(d) {
        return width - x(d.enrols);
    });

var texts = bars.append("text")
    .attr("x", function(d) {
        return width - x(d.enrols) - 15;
    })
    .attr("y", y.rangeBand() / 2)
    .attr("dy", ".35em")
    .text(function(d) {
        return d.enrols;
    });

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);

只留下真正改变的内容:

  • x比例的域名,
  • 绑定到组的数据,
  • 矩形和文本的过渡。

它会更小:

function render(selection) {

    // filter data
    var subset = data.filter(function(d) {
        return d.group === selection
    });

    // scale range of data
    x.domain([0, d3.max(subset, function(d) {
        return d.enrols;
    })]);

    bars.data(subset, function(d) {
        return d.month;
    });

    rects.transition().duration(500).attr("width", function(d) {
        return width - x(this.parentNode.__data__.enrols);
    });

    texts.transition().duration(500).attr("x", function(d) {
            return width - x(this.parentNode.__data__.enrols) - 15;
        })
        .text(function(d) {
            return this.parentNode.__data__.enrols;
        });

}

以下是您的CodePen:http://codepen.io/anon/pen/qqBzWQ?editors=0010

PS:如果你摆脱这些组,直接附加矩形和文本,这将更容易。

答案 1 :(得分:0)

您的条形图只需要一个非常简单的修改。这是缺失的拼图:

当我们使用data()函数时,D3会在现有DOM元素和数据值之间执行join operation。您将此操作的结果存储在变量bars上。我们可以在bars上调用几种方法。

方法exit()返回将要删除的元素。如果我们在DOM上有3个条形,并且我们使用只有2个数据值的数组调用data()函数,exit()将返回将被删除的条形。

同样,方法enter()返回将要添加的元素。如果DOM上有2个条形,并且我们使用包含3个数据值的数组调用data()函数,enter()将返回将添加的新条形。

然后要更新的酒吧怎么样?好吧,它们是存储在bars变量中的内容!

将其翻译成代码非常简单:

此:

bars.enter()
  .append("g")
  ...

变成了这个:

var newBars = bars.enter()
  .append("g")
  ...

然后每次出现:

bars.append(...)

变成了这个:

newBars.append(...)

每次有新栏时,我们{DOM}都要{DOM}。

每次我们需要更新一个条形码时,我们都会从DOM中append

select指的是要更新的栏。所以我们添加此代码来更新现有的栏:

bars