d3 折线图和面积图未使用新数据数组更新
d3 line and area charts not updating with new data array
我写了一个可重复使用的 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 工作原理的基本知识。
如何在传递新数据数组时使图表正确更新?
我今天确实遇到了同样的问题。所以我也许能帮上忙。我注意到您正在调用 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);
如果这不能解决问题,可能是因为您正在使用每秒同时启动的两个 setTimeout 进行更新。所以第二个数据在这里立即覆盖第一个:
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
更新线路路径的地方,通过
plots.selectAll("path")
需要
plots.select("path")
查看 mbostock's explanation 细微但关键的区别。
这是一个 working fiddle,它还添加了第二条路径,以验证它是否适用于多个地块。
我写了一个可重复使用的 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 工作原理的基本知识。
如何在传递新数据数组时使图表正确更新?
我今天确实遇到了同样的问题。所以我也许能帮上忙。我注意到您正在调用 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);
如果这不能解决问题,可能是因为您正在使用每秒同时启动的两个 setTimeout 进行更新。所以第二个数据在这里立即覆盖第一个:
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
更新线路路径的地方,通过
plots.selectAll("path")
需要
plots.select("path")
查看 mbostock's explanation 细微但关键的区别。
这是一个 working fiddle,它还添加了第二条路径,以验证它是否适用于多个地块。