【问题标题】:d3 line and area charts not updating with new data arrayd3 折线图和面积图未使用新数据数组更新
【发布时间】:2015-01-21 00:54:07
【问题描述】:

我写了一个可重复使用的 d3 折线图(代码如下)。不幸的是,它只有在传递给它的数据数组更新时才能正确更新;如果它被传递一个新的数据数组,它根本不会更新 - 你可以在这个 jsfiddle 中看到它。

这里是html,主要是嵌入的demo调用脚本:

<style>
  path { stroke: purple; stroke-width: 2px; fill: none; }
</style>
<body>
<div id="demo"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="demoChart.js"></script>
<script>
  var chart = demoChart();
  var pts = [[[0,0],[200,0.25],[500,1]]];

  d3.select("#demo").datum(pts).call(chart);
  setTimeout(function() {
    console.log("Modifying data array");
    pts[0][2][1] = 0.5;
    d3.select("#demo").datum(pts).call(chart);
  },1000);

  setTimeout(function() {
    console.log("Passing new data array");
    d3.select("#demo").datum([[[0,1],[200,0.45],[500,0]]]).call(chart);
  },2000);
</script>

您可以看到它第二次调用chart 时直接更新了数据数组中的一个点 (pts[0][3][1] = 0.5),并且图表的动画效果正常。第三次传递一个新的数据数组,图表没有变化。

这是demoChart.js 代码(基于reusable charts 模式):

function demoChart() {

    function xs(d) { return xScale(d[0]) }
    function ys(d) { return yScale(d[1]) }

    var xScale = d3.scale.linear().domain([0, 500]).range([0, 400]),
        yScale = d3.scale.linear().domain([0, 1]).range([400, 0]),
        line = d3.svg.line().x(xs).y(ys);

    function chart(selection) {
        selection.each(function(data) {
            console.log("passed data: ", data);

            // Select the svg element, if it exists; otherwise create it
            var svg = d3.select(this).selectAll("svg").data([1]);
            var svgGEnter = svg.enter().append("svg").append("g");

            // Select/create/remove plots for each y, with the data
            var plots = svg.select("g").selectAll(".plot").data(data);
            plots.exit().remove();
            var plotsEnter = plots.enter().append("g").attr("class","plot");
            plotsEnter.append("path");

            // Update the line paths
            plots.selectAll("path")
                .transition()
                    .attr("d", function(d,i) {
                        console.log("transitioning line with data: ", d);
                        return line.apply(this, arguments);
                    });

            svg.attr("width", 400).attr("height", 400);

        });
    }

    return chart;
}

我怀疑我遗漏了一些关于 d3 工作原理的基本知识。

如何在传递新数据数组时正确更新图表?

【问题讨论】:

    标签: javascript d3.js charts


    【解决方案1】:

    在哪里更新线路路径,通过

    plots.selectAll("path")
    

    应该是

    plots.select("path")
    

    请参阅mbostock's explanation 了解细微但至关重要的区别。

    这是一个working fiddle,它还添加了第二条路径,以验证它是否可以跨地块工作。

    【讨论】:

    • 就是这样!谢谢,感谢工作示例和 mbostock 解释的链接。我还没有完全掌握如何在selectselectAll 之间进行选择,但我会从现在开始关注它......
    【解决方案2】:

    我今天实际上遇到了同样的问题。所以我也许能帮上忙。我注意到您正在调用 setTimeout 来更新 html 中的数据。

    您似乎在 setTimeout 内调用 chart()。 唯一的问题是你没有重置你的范围。

    相反,您应该尝试调用 demoChart(),或在您的 chart() 中添加新范围。您的范围是:

    function xs(d) { return xScale(d[0]) }
    function ys(d) { return yScale(d[1]) }
    
    var xScale = d3.scale.linear().domain([0, 500]).range([0, 400]),
        yScale = d3.scale.linear().domain([0, 1]).range([400, 0]),
        line = d3.svg.line().x(xs).y(ys);
    

    如果这不能解决问题,可能是因为您正在使用每秒同时启动的两个 setTimeouts 进行更新。所以第二个数据在这里立即覆盖第一个:

    setTimeout(function() {
      console.log("Modifying data array");
      pts[0][2][1] = 0.5;
      d3.select("#demo").datum(pts).call(chart);
    },1000);
    
    setTimeout(function() {
      console.log("Passing new data array");
      d3.select("#demo").datum([[[0,1],[200,0.45],[500,0]]]).call(chart);
    },1000);
    

    这是一篇我觉得很有帮助的文章:http://www.d3noob.org/2013/02/update-d3js-data-dynamically-button.html

    【讨论】:

    • 谢谢泰勒。是的,延迟为 1000 的第二次超时很糟糕,它应该是 2000(我现在已经编辑过了)——结果是一样的。另外,我应该说,我只是在这里的示例中使用了 setTimeout,在我的实际应用程序中,它是对事件的响应。 meetamit 的回答似乎解决了这个问题。
    猜你喜欢
    • 2014-08-30
    • 2012-01-22
    • 2018-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 2013-02-02
    • 2016-03-21
    相关资源
    最近更新 更多