我正在研究个人财务的可视化,以学习 d3,感觉像是一个有用的项目。我已经设法按照我的意愿制作每个月的图表(每天+或减去)。现在我希望能够从一个月更改为下一个月。如果旧月份(更新前)的天数(又名数据点)比新月份(更新后)多,则此方法有效。如果旧数据点少于新数据点,则会在图表顶部添加额外的数据点。我将条形图中的每个数据点作为一个组添加(条形图本身、数据标签 + 日期标签)。我正在为每个新的一天向下翻译整个小组。我需要弄清楚的是,更新后我的数据点是多了还是少了,如果少了,我需要向下翻译新的数据点。 你知道我的意思? 这是我最初添加条形图的代码:
bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
//bar
//grey background bars
bar.append("rect")
.attr("class", "backgroundBar")
.attr("x", 10)
.attr("width", (width-30))
.attr("height", barHeight-1)
.attr("fill", "#dddddd")
.attr("fill-opacity", "0.3");
//dateLabel
bar.append("text")
.attr("class", "dateLabel")
.attr("x", width/2-20)
.attr("y", barHeight-5)
.attr("fill", "black")
.text(function(d){ return d.key})
bar.append("rect")
.attr("class", "bar")
.attr("x", function(d) { if(scale(d.values.total)<0){return width/2+widthDateLabel;}else{return width/2-scale(d.values.total)-widthDateLabel;}})
.attr("width", function(d) { return Math.abs(scale(d.values.total)); })
.attr("height", barHeight - 1)
.attr("fill", function(d) { if(scale(d.values.total)<0){ return "DeepPink"}else{return "MediumSeaGreen"}});
//BarLabel
bar.append("text")
.attr("class", "barLabel")
.attr("x",function(d) { if(scale(d.values.total)<0){return window.width/2-scale(d.values.total)+5+widthDateLabel;}else{return window.width/2-scale(d.values.total)-5-widthDateLabel;}})
.attr("y", barHeight/2)
.attr("dy", ".35em")
.attr("text-anchor", function(d) { if(scale(d.values.total)<0){ return "start"}else{return "end"}})
.attr("fill", function(d) { if(scale(d.values.total)<0){ return "DeepPink"}else{return "MediumSeaGreen"}})
.text(function(d) { return Math.round(d.values.total*100)/100; });
我显然不能与组一起工作,而是平移每个 y 坐标,但这感觉像是一个肮脏的解决方案,不是吗? 这是问题的屏幕截图:
编辑: 这是我当前的更新功能。有点像,但会在屏幕截图中产生结果
//update the bar itself
var bar=chartgroups.selectAll(".bar")
.data(data);
bar.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { if(scale(d.values.total)<0){return width/2+widthDateLabel;}else{return width/2-scale(d.values.total)-widthDateLabel;}})
.attr("width", function(d) { return Math.abs(scale(d.values.total)); })
.attr("height", barHeight - 1)
.attr("fill", function(d) { if(scale(d.values.total)<0){ return "DeepPink"}else{return "MediumSeaGreen"}});
bar.exit().remove();
bar
.transition().duration(750)
.attr("height", barHeight - 1)
.attr("x", function(d) { if(scale(d.values.total)<0){return width/2+widthDateLabel;}else{return width/2-scale(d.values.total)-widthDateLabel;}})
.attr("width", function(d) { return Math.abs(scale(d.values.total)); })
.attr("fill", function(d) { if(scale(d.values.total)<0){ return "DeepPink"}else{return "MediumSeaGreen"}});
//update the barLabel
var barLabel=chart.selectAll(".barLabel").data(data);
barLabel.enter().append("text")
.attr("x",function(d) { if(scale(d.values.total)<0){return window.width/2-scale(d.values.total)+5+widthDateLabel;}else{return window.width/2-scale(d.values.total)-5-widthDateLabel;}})
.attr("y", barHeight/2)
.attr("dy", ".35em")
.attr("text-anchor", function(d) { if(scale(d.values.total)<0){ return "start"}else{return "end"}})
.attr("fill", function(d) { if(scale(d.values.total)<0){ return "DeepPink"}else{return "MediumSeaGreen"}})
.text(function(d) { return Math.round(d.values.total*100)/100; });
barLabel.exit().remove();
barLabel
.transition().duration(750)
.attr("x",function(d) { if(scale(d.values.total)<0){return window.width/2-scale(d.values.total)+5+widthDateLabel;}else{return window.width/2-scale(d.values.total)-5-widthDateLabel;}})
.attr("y", barHeight/2)
.attr("text-anchor", function(d) { if(scale(d.values.total)<0){ return "start"}else{return "end"}})
.attr("fill", function(d) { if(scale(d.values.total)<0){ return "DeepPink"}else{return "MediumSeaGreen"}})
.text(function(d) { return Math.round(d.values.total*100)/100; });
//update dates
var dateLabel=chart.selectAll(".dateLabel").data(data);
dateLabel.enter().append("text")
.attr("class", "dateLabel")
.attr("x", width/2-20)
.attr("y", barHeight-5)
.attr("fill", "black")
.text(function(d){ return d.key})
dateLabel.exit().remove();
dateLabel
.transition().duration(750)
.text(function(d){ return d.key})
.attr("y", barHeight-5)
//update background bars
var backgroundBar=chart.selectAll(".backgroundBar").data(data);
backgroundBar.enter().append("rect")
.attr("class", "backgroundBar")
.attr("x", 10)
.attr("width", (width-30))
.attr("height", barHeight-1)
.attr("fill", "#dddddd")
.attr("fill-opacity", "0.3");
backgroundBar.exit().remove();
backgroundBar
.transition().duration(750)
.attr("height", barHeight-1)
最佳答案
这是一个工作代码片段,当您按下按钮时,它会在两个月之间转换。它非常接近您的代码。只是更新发生方式的细微差别。
var january = [
{ "day": "1/1/2015", "value": 105},
{ "day": "1/2/2015", "value": -119},
{ "day": "1/3/2015", "value": 148},
{ "day": "1/4/2015", "value": -161},
{ "day": "1/5/2015", "value": 142},
{ "day": "1/6/2015", "value": -105},
{ "day": "1/7/2015", "value": 131},
{ "day": "1/8/2015", "value": 42},
{ "day": "1/9/2015", "value": -74},
{ "day": "1/10/2015", "value": 175},
{ "day": "1/11/2015", "value": 154},
{ "day": "1/12/2015", "value": 164},
{ "day": "1/13/2015", "value": 31},
{ "day": "1/14/2015", "value": 81},
{ "day": "1/15/2015", "value": 5},
{ "day": "1/16/2015", "value": -194},
{ "day": "1/17/2015", "value": -90},
{ "day": "1/18/2015", "value": 8},
{ "day": "1/19/2015", "value": 161},
{ "day": "1/20/2015", "value": -99},
{ "day": "1/21/2015", "value": -42},
{ "day": "1/22/2015", "value": -145},
{ "day": "1/23/2015", "value": 168},
{ "day": "1/24/2015", "value": -44},
{ "day": "1/25/2015", "value": -2},
{ "day": "1/26/2015", "value": 177},
{ "day": "1/27/2015", "value": -21},
{ "day": "1/28/2015", "value": -29},
{ "day": "1/29/2015", "value": 192},
{ "day": "1/30/2015", "value": 199},
{ "day": "1/31/2015", "value": 79}
];
var february = [
{ "day": "2/1/2015", "value": "36"},
{ "day": "2/2/2015", "value": "151"},
{ "day": "2/3/2015", "value": "-157"},
{ "day": "2/4/2015", "value": "39"},
{ "day": "2/5/2015", "value": "-69"},
{ "day": "2/6/2015", "value": "97"},
{ "day": "2/7/2015", "value": "-55"},
{ "day": "2/8/2015", "value": "156"},
{ "day": "2/9/2015", "value": "151"},
{ "day": "2/10/2015", "value": "-72"},
{ "day": "2/11/2015", "value": "-17"},
{ "day": "2/12/2015", "value": "154"},
{ "day": "2/13/2015", "value": "77"},
{ "day": "2/14/2015", "value": "80"},
{ "day": "2/15/2015", "value": "-112"},
{ "day": "2/16/2015", "value": "-155"},
{ "day": "2/17/2015", "value": "21"},
{ "day": "2/18/2015", "value": "-63"},
{ "day": "2/19/2015", "value": "-136"},
{ "day": "2/20/2015", "value": "127"},
{ "day": "2/21/2015", "value": "-43"},
{ "day": "2/22/2015", "value": "-66"},
{ "day": "2/23/2015", "value": "105"},
{ "day": "2/24/2015", "value": "2"},
{ "day": "2/25/2015", "value": "-92"},
{ "day": "2/26/2015", "value": "-160"},
{ "day": "2/27/2015", "value": "13"},
{ "day": "2/28/2015", "value": "163"}
];
function updateData(data) {
var maxValue = d3.max(data, function(d) { return Math.abs(d.value); });
scaleX.domain([0, maxValue]);
//update background bars
var backgroundBar = chartgroups.selectAll(".backgroundBar").data(data);
backgroundBar.enter().append("rect");
backgroundBar.attr("class", "backgroundBar")
.attr("x", 0 - margin)
.attr("y", function (d, i) {
return (i * barHeight);
})
.attr("width", chartWidth*2 + margin)
.attr("height", barHeight - 1)
.attr("fill", "#dddddd")
.attr("fill-opacity", "0.3");
backgroundBar.exit().remove();
var bars = chartgroups.selectAll(".bar")
.data(data);
bars.enter().append("rect")
.attr("fill", function (d) {
if (d.value < 0) {
return "DeepPink"
} else {
return "MediumSeaGreen"
}
});
bars.attr("class", "bar")
.transition()
.duration(1000)
.attr("x", function (d) {
if (d.value < 0) {
return negativeStart;
} else {
return scaleWidth - scaleX(d.value);
}
}).attr("y", function (d, i) {
return i * barHeight;
})
.attr("width", function (d) {
return scaleX(Math.abs(d.value));
})
.attr("height", barHeight - 1)
.attr("fill", function (d) {
if (d.value < 0) {
return "DeepPink"
} else {
return "MediumSeaGreen"
}
});
bars.exit().remove();
//update the barLabel
var barLabel = chartgroups.selectAll(".barLabel").data(data);
barLabel.enter().append("text");
barLabel.attr("class", "barLabel")
.transition()
.duration(1000)
.attr("x", function (d) {
if (d.value < 0) {
return negativeStart + scaleX(Math.abs(d.value));
} else {
return scaleWidth - scaleX(d.value);
}
})
.attr("y", function (d, i) {
return (i * barHeight) + (barHeight/2);
})
.attr("dy", ".35em")
.attr("text-anchor", function (d) {
if (d.value < 0) {
return "start"
} else {
return "end"
}
})
.attr("fill", function (d) {
if (d.value < 0) {
return "DeepPink"
} else {
return "MediumSeaGreen"
}
})
.text(function (d) {
return Math.round(d.value * 100) / 100;
});
barLabel.exit().remove();
// //update dates
var dateLabel = chartgroups.selectAll(".dateLabel").data(data);
dateLabel.enter().append("text");
dateLabel.attr("class", "dateLabel")
.attr("fill", "black")
.transition()
.duration(1000)
.attr("x", scaleWidth)
.attr("y", function (d, i) {
return (i * barHeight) + (barHeight/2) + 1;
})
.text(function (d) {
return d.day;
});
dateLabel.exit().remove();
}
var container = d3.select(".chart");
var margin = 60;
var containerWidth = container.node().getBoundingClientRect().width;
var chartWidth = containerWidth - (2*margin);
var barHeight = 20;
var dateLabelWidth = 80;
var chartHeight = 31 * barHeight;
var scaleWidth = (chartWidth - dateLabelWidth) / 2;
var negativeStart = chartWidth - scaleWidth;
var scaleX = d3.scale.linear()
.range([0, scaleWidth]);
var chartgroups = container.append("svg")
.attr("width", containerWidth)
.attr("height", chartHeight)
.append("g")
.attr("transform", "translate(" + margin + "," + 0 + ")");
updateData(january);
d3.select(".january").on("click", function() {
updateData(january);
});
d3.select(".february").on("click", function() {
updateData(february);
});
.chart {
width: 100%;
}
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
</head>
<body>
<button type="button" class="january">January</button>
<button type="button" class="february">February</button>
<div class="chart">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
</body>
</html>
通常负数会在左边。您也可以使用 y 比例尺,并且可能有更简洁的方法来创建 x 比例尺。
关于javascript - d3月度数据集-更新数据-添加新组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36518131/