简体   繁体   English

d3.js-多个折线图不会更新圆

[英]d3.js - Multiple line chart doesn't update circles

I'm working on a d3 chart (Multiple line chart). 我正在处理d3图表(多条折线图)。
I'm trying to represent a stock prediction, so basically the chart contains two lines: stock values line and an other one for my prediction. 我试图表示一个股票预测,所以基本上该图表包含两条线:股票价值线和另一条用于我的预测。

The prediction is monthly, all days of month are represented in the chart. 预测是每月一次,图表中表示每月的所有天数。
In order to choose the month I have added a dropdown menu. 为了选择月份,我添加了一个下拉菜单。

I appended a circle on each daily data, and works well for the first time. 我在每个每日数据上都加上了一个圆圈,并且效果很好。 When user tries to change the month, the old circles are not updated, but the new ones are added. 当用户尝试更改月份时,不会更新旧圈子,但会添加新圈子。

Follow the code about circles: 请遵循有关圈子的代码:

topicEnter.append("g").selectAll("circle")
    .data(function(d){return d.values})
    .enter()
    .append("circle")
    .attr("r", 5)
    .attr("cx", function(dd){return x(dd.date)})
    .attr("cy", function(dd){return y(dd.probability)})
    .attr("fill", "none")
    .attr("stroke", "black");

I have done a fiddle to understand better the situation and in order to show code. 我做了一个小提琴,以更好地了解情况并显示代码。

What am I missing here? 我在这里想念什么? Why don't the circles update themself with the lines? 圆为什么不用线更新自己?

The problem is your update cycle, but there are a good number of examples of the enter, update, exit process in d3. 问题是您的更新周期,但是在d3中有很多输入,更新,退出过程的示例。

But essentially: 但本质上:

  1. You append a new g element for each batch of circles, which means you have an empty selection (no circles are in that g yet) each time and each data point is appended (and none are removed). 您为每一批圆附加一个新的g元素,这意味着您每次都有一个空选择(g中没有圆),并且每个数据点都被附加(没有删除)。 You don't need this extra append. 您不需要额外的附件。 Take a look at the DOM structure on each append in your existing code. 看一下现有代码中每个追加的DOM结构。

  2. Your enter() selection returns new elements - not modified elements. 您的enter()选择返回新元素-而不是修改后的元素。 So if your total number of elements remains the same you will have an empty enter() selection. 因此,如果元素总数保持不变,则将有一个空的enter()选择。 You'll want to update existing elements separately (alternatively, remove them all and append them all every time). 您需要单独更新现有元素(或者,将其全部删除,并每次都附加它们)。

You'll want something closer to this : 您将需要更接近此的东西:

// set the data
   circles =  topic.selectAll("circle")
      .data(function(d){return d.values});
// update existing circles      
   circles.attr("cx", function(dd){return x(dd.date)})
      .attr("cy", function(dd){return y(dd.probability)});
// add new circles     
   circles.enter()
      .append("circle")
      .attr("r", 5)
      .attr("cx", function(dd){return x(dd.date)})
      .attr("cy", function(dd){return y(dd.probability)})
      .attr("fill", "none")
      .attr("stroke", "black");
   // remove excess circles   
   circles.exit().remove();

You'll likely also want to revise the lines that append the lines to reflect the enter, update, exit cycle in d3. 您可能还希望修改添加这些行的行,以反映d3中的输入,更新,退出周期。

To solve the issue about circles not updating you can do the following: 要解决有关圈子未更新的问题,您可以执行以下操作:

function update(topics) {
    // Calculate min and max values with arrow functions
  const minValue = d3.min(topics, t => d3.min(t.values, v => v.probability));
  const maxValue = d3.max(topics, t => d3.max(t.values, v => v.probability));
  y.domain([minValue, maxValue]);
  x2.domain(x.domain());
  y2.domain(y.domain());
  // update axes
  d3.transition(svg).select('.y.axis').call(yAxis);
  d3.transition(svg).select('.x.axis').call(xAxis);
  // Update context
  var contextUpdate = context.selectAll(".topic").data(topics);
  contextUpdate.exit().remove();
  contextUpdate.select('path')
  .transition().duration(600)
  .call(drawCtxPath);
  contextUpdate.enter().append('g') // append new topics
    .attr('class', 'topic')
    .append('path').call(drawCtxPath);
  // New data join
  var focusUpdate = focus.selectAll('.topic').data(topics);
  // Remove extra topics not found in data
  focusUpdate.exit().remove(); //remove topics
  // Update paths
  focusUpdate.select('path')
  .transition().duration(600)
  .call(drawPath)
  // Update circles
  var circlesUpdate = focusUpdate
    .selectAll('.topic-circle')
    .data(d => d.values);
  circlesUpdate.exit().remove();
  circlesUpdate.transition().duration(600).call(drawCircle);
  circlesUpdate.enter().append('circle').call(drawCircle);
  // Add new topics
  var newTopics = focusUpdate.enter().append('g') // append new topics
    .attr('class', 'topic');
  // Add new paths
  newTopics.append('path').call(drawPath)
  // Add new circles
  newTopics.selectAll('.topic-circle')
    .data(d => d.values)
    .enter()
    .append('circle')
    .call(drawCircle);
}

With these helper functions to reduce code duplication: 使用这些帮助程序功能可减少代码重复:

function drawCtxPath(path) {
    path.attr("d", d => line2(d.values))
    .style("stroke", d => color(d.name));
}
function drawPath(path) {
    path.attr("d", d => line(d.values))
    .attr('clip-path', 'url(#clip)')
    .style("stroke", d => color(d.name));
}
function drawCircle(circle) {
    circle.attr('class', 'topic-circle')
    .attr('clip-path', 'url(#clip)')
    .attr("r", d => 5)
    .attr("cx", d => x(d.date))
    .attr("cy", d => y(d.probability))
    .attr("fill", "none")
    .attr("stroke", "black");
}

I think there are some additional issues in your code, when you select the same month twice you get an error, we can fix that by doing the following: 我认为您的代码中还有一些其他问题,当您两次选择同一个月时遇到错误,我们可以通过以下操作来解决:

d3.select('#month_chart').on("change", function() {
  // Get selected value of the select
  var month = this.options[this.selectedIndex].value;
  // Since you have hardcoded data we need to return a new array
  // This is why if you select the same month twice your code breaks
  // since parseDate will fail since the data will be already parsed
  // the second time
  var monthData = get_monthly_data(month).map(d => {
    return {
      date: parseDate(d.date),
      predicted_bool: d.predicted_bool,
      target: d.target
    };
  });
  // Lets use arrow functions!
  var keys = d3.keys(monthData[0]).filter(k => k !== 'date');
  color.domain(keys);
  // More arrow functions!
  var topics = keys.map(key => {
    return {
      name: key,
      values: monthData.map(d => {
        return {
          date: d.date,
          probability: +d[key]
        };
      })
    };
  });
  x.domain(d3.extent(monthData, d => d.date));
  update(topics);
});

// A good ol' switch 
function get_monthly_data(month) {
  switch (month) {
    case 'gennaio':
      return data_1;
    case 'febbraio':
      return data_2;
    case 'marzo':
      return data_3;
    default:
      return data_1;
  }
}

Working jsfiddle: 工作jsfiddle:

https://jsfiddle.net/g699scgt/37/ https://jsfiddle.net/g699scgt/37/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM