问题
Based on this stackoverflow answer I was trying to copy essential parts to get proper scale for timeline. I use multibarcharts for multible graphs, from few records to hundreds with X-axis having data from 1930 to today.
I've copied it like this, but I have two issues:
- Last bar is always outside of graph
bars overlap, which I can partly fix by altering
numTicks
, but isn't there a better way?<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script> <script type="text/javascript"> nv.addGraph(function() { var data = [{ "values": [ { x: new Date("1999-12-01"), y: 42.27 } , { x: new Date("2000-12-01"), y: 41.73 } , { x: new Date("2001-12-01"), y: 41.34 } , { x: new Date("2002-12-01"), y: 41.84 } , { x: new Date("2003-12-01"), y: 43.93 } , { x: new Date("2004-12-01"), y: 42.18 } , { x: new Date("2005-12-01"), y: 42.31 } , { x: new Date("2006-12-01"), y: 43.14 } , { x: new Date("2007-12-01"), y: 43.24 } , { x: new Date("2008-12-01"), y: 39.30 } , { x: new Date("2009-12-01"), y: 43.80 } , { x: new Date("2010-12-01"), y: 44.10 } , { x: new Date("2011-12-01"), y: 54.10 } , { x: new Date("2012-12-01"), y: 62.10 } , { x: new Date("2013-12-01"), y: 56.70 } , { x: new Date("2014-12-01"), y: 45 } , { x: new Date("2015-12-01"), y: 55.60 } , { x: new Date("2026-12-01"), y: 54.40 } , { x: new Date("2027-12-01"), y: 57 } ], "bar": true, "key": "Payout Ratio" }]; var chart = nv.models.multiBarChart(), container = d3.select('#payout_ratio_chart svg'), availableWidth, numTicks = data[0].values.length, xScale = d3.time.scale(); function updateAvailableWidth() { availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right; } updateAvailableWidth(); nv.utils.windowResize(updateAvailableWidth); xScale.rangeBands = xScale.range; xScale.rangeBand = function() { return (1 - chart.groupSpacing()) * availableWidth / numTicks; }; chart.multibar.xScale(xScale); var last_date = data[0].values[data[0].values.length-1].x; last_date.setMonth(last_date.getMonth() + 10); chart.xDomain([data[0].values[0].x, last_date]); chart.xAxis.tickFormat(function(d){ return d3.time.format('%b %Y')(new Date(d)); }); chart.yAxis.tickFormat(d3.format(',f')); chart.showControls(false); container.datum(data).transition().duration(500).call(chart); nv.utils.windowResize(chart.update); return chart; }); </script>
https://jsfiddle.net/lucas03/poamvfke/4/
回答1:
Checkout the code below which was derived from your posted code:
nv.addGraph(function() {
var data = [{
"values": [
{
x: new Date("1999-12-01"),
y: 42.27
},
{
x: new Date("2000-12-01"),
y: 41.73
},
{
x: new Date("2001-12-01"),
y: 41.34
},
{
x: new Date("2002-12-01"),
y: 41.84
},
{
x: new Date("2003-12-01"),
y: 43.93
},
{
x: new Date("2004-12-01"),
y: 42.18
},
{
x: new Date("2005-12-01"),
y: 42.31
},
{
x: new Date("2006-12-01"),
y: 43.14
},
{
x: new Date("2007-12-01"),
y: 43.24
},
{
x: new Date("2008-12-01"),
y: 39.30
},
{
x: new Date("2009-12-01"),
y: 43.80
},
{
x: new Date("2010-12-01"),
y: 44.10
},
{
x: new Date("2011-12-01"),
y: 54.10
},
{
x: new Date("2012-12-01"),
y: 62.10
},
{
x: new Date("2013-12-01"),
y: 56.70
},
{
x: new Date("2014-12-01"),
y: 45
},
{
x: new Date("2015-12-01"),
y: 55.60
},
{
x: new Date("2026-12-01"),
y: 54.40
},
{
x: new Date("2027-12-01"),
y: 57
}
],
"bar": true,
"key": "Payout Ratio"
}];
var fDate = data[0].values[0].x,
lDate = new Date(data[0].values[data[0].values.length - 1].x);
lDate.setFullYear(lDate.getFullYear() + 1);
var chart = nv.models.multiBarChart()
.showControls(false)
.reduceXTicks(false)
.rotateLabels(-45),
container = d3.select('#payout_ratio_chart svg'),
availableWidth,
numTicks = (lDate.getFullYear() - fDate.getFullYear()) + 1,
xScale = d3.time.scale();
function updateAvailableWidth() {
availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
}
updateAvailableWidth();
xScale.rangeBands = xScale.range;
xScale.rangeBand = function() {
return (1 - chart.groupSpacing()) * availableWidth / numTicks;
};
chart.multibar.xScale(xScale);
chart.xDomain([fDate, lDate]);
chart.xAxis.tickFormat(function(d) {
return d3.time.format('%b %Y')(new Date(d));
});
chart.yAxis.tickFormat(d3.format(',f'));
container.datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(function() {
updateAvailableWidth();
chart.update();
});
return chart;
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.css" rel="stylesheet"/>
<div id="payout_ratio_chart">
<svg style="width:100%;height:400px" />
</div>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script>
Fixes:
Only difference between your code and fixed code is how fDate
, lDate
and numTicks
are calculated.
fDate
is your start date while lDate
is last date in data for the following year. Lastly, numTicks
is difference between years in fDate
and lDate
.
Find jsfiddle here.
回答2:
You have a bar chart so you have to insert data points for not existing dates.
Here I have hard coded but you can write some logic to add missing dates. For some other data serie you can have values for the missing dates of this serie.
nvd3 draws these bars with a minimal height of 1px, so add a style to hide these small bars
.nvd3 .nv-groups rect[height="1"] {
opacity: 0;
}
nv.addGraph(function() {
var data = [{
"values": [
{ x: new Date("1999-12-01"), y: 42.27 },
{ x: new Date("2000-12-01"), y: 41.73 },
{ x: new Date("2001-12-01"), y: 41.34 },
{ x: new Date("2002-12-01"), y: 41.84 },
{ x: new Date("2003-12-01"), y: 43.93 },
{ x: new Date("2004-12-01"), y: 42.18 },
{ x: new Date("2005-12-01"), y: 42.31 },
{ x: new Date("2006-12-01"), y: 43.14 },
{ x: new Date("2007-12-01"), y: 43.24 },
{ x: new Date("2008-12-01"), y: 39.30 },
{ x: new Date("2009-12-01"), y: 43.80 },
{ x: new Date("2010-12-01"), y: 44.10 },
{ x: new Date("2011-12-01"), y: 54.10 },
{ x: new Date("2012-12-01"), y: 62.10 },
{ x: new Date("2013-12-01"), y: 56.70 },
{ x: new Date("2014-12-01"), y: 45 },
{ x: new Date("2015-12-01"), y: 55.60 },
{ x: new Date("2016-12-01"), y: 0 },
{ x: new Date("2017-12-01"), y: 0 },
{ x: new Date("2018-12-01"), y: 0 },
{ x: new Date("2019-12-01"), y: 0 },
{ x: new Date("2020-12-01"), y: 0 },
{ x: new Date("2021-12-01"), y: 0 },
{ x: new Date("2022-12-01"), y: 0 },
{ x: new Date("2023-12-01"), y: 0 },
{ x: new Date("2024-12-01"), y: 0 },
{ x: new Date("2025-12-01"), y: 0 },
{ x: new Date("2026-12-01"), y: 54.40 },
{ x: new Date("2027-12-01"), y: 57 }
],
"bar": true,
"key": "Payout Ratio"
}];
var chart = nv.models.multiBarChart(),
container = d3.select('#payout_ratio_chart svg');
chart.xAxis.tickFormat(function(d) {
return d3.time.format('%b %Y')(new Date(d));
});
chart.yAxis.tickFormat(d3.format(',f'));
chart.showControls(false);
container.datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
.nvd3 .nv-groups rect[height="1"] {
opacity: 0;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.css" rel="stylesheet"/>
<div id="payout_ratio_chart">
<svg style="width:100%;height:400px" />
</div>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script>
来源:https://stackoverflow.com/questions/53575721/nvd3-multibarchart-last-bar-is-hidden-not-visible