转换D3退出多系列折线图标签

时间:2016-04-29 23:40:07

标签: javascript d3.js

我正在尝试创建一个多系列折线图(基于Mike Bostock示例),但是在多个数据集之间进行转换。我已经获得了进出的线条,但是每条线条的标签在它们应该消失后仍然存在。 Screenshot at this link.

此外,线路以奇怪的方式过渡;几乎就像他们只是在同一条线上创建一个新的(Second screenshot at this link)

以下是我的代码的相关部分(我绘制线条并添加标签):

 var line = d3.svg.line()
        .interpolate("basis")
        .x(function(d) { return x(d.Date); })
        .y(function(d) { return y(d.candidate); });

    var person = svg_multi.selectAll(".candidate")
        .data(people);

    var personGroups = person.enter()
        .append("g")
        .attr("class", "candidate");

    person
        .enter().append("g")
        .attr("class", "candidate");

    personGroups.append("path")
        .attr("class", "line")
        .attr("d", function(d) { return line(d.values); })
        .style("stroke", function(d) { return color(d.name); });

    var personUpdate = d3.transition(person);

    personUpdate.select("path")
        .transition()
        .duration(1000)
        .attr("d", function(d) {
            return line(d.values);
        });

    person
        .append("text")
        .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
        .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; })
        .attr("x", 3)
        .attr("dy", ".35em")
        .text(function(d) { return d.name; });

    person.selectAll("text").transition()
        .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; });

    person.exit().remove();

1 个答案:

答案 0 :(得分:0)

每次渲染时都会向每个人追加一个新文本元素,而不是删除旧文本元素。据推测,每次要使用新数据绘制图表时,您发布的代码都会运行,因此每次最终都会有更多的文本元素,而不是更新现有的文本元素。您只需要附加“输入”选项。您在路径元素上执行了此操作,因此您只需要使文本更像路径。我已用注释更新了您的示例,以突出显示我更改的内容。

var line = d3.svg.line()
  .interpolate("basis")
  .x(function(d) { return x(d.Date); })
  .y(function(d) { return y(d.candidate); });

var person = svg_multi.selectAll(".candidate")
  // I added a key function here to make sure you always update the same
  // line for every person. This ensures that when you re draw with different
  // data, the line for Trump doesn't become the line for Sanders, for example.
  .data(people, function(d) { return d.name});

var personGroups = person.enter()
  .append("g")
  .attr("class", "candidate");

// This isn't needed. The path and text get appended to the group above,
// so this one just sits empty and clutters the DOM
// person
//   .enter().append("g")
//   .attr("class", "candidate");

personGroups.append("path")
  .attr("class", "line")
  // You do this down below, so no need to duplicate it here
  // .attr("d", function(d) { return line(d.values); })
  .style("stroke", function(d) { return color(d.name); });
  
// Append the text element to only new groups in the enter selection  
personGroups.append("text")
  // Set any static attributes here that don't update on data
  .attr("x", 3)
  .attr("dy", ".35em");

var personUpdate = d3.transition(person);

personUpdate.select("path")
  .transition()
  .duration(1000)
  .attr("d", function(d) {
    return line(d.values);
  });

person.select("text")
  // You don't have to do this datum call because the text element will have
  // the same data as its parent, but it does make it easier to get to the last
  // value in the list, so you can do it if you want
  .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
  .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; })
  .text(function(d) { return d.name; });

// Remove this. You don't need it anymore since you are updating the text above
// person.selectAll("text").transition()
//   .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; });

person.exit().remove();

问题的关键在于personGroups.append('text')而不是person.append('text')。剩下的只是我过火了,并指出了其他一些改进代码的方法,这些方法应该更容易理解和维护。