data: [{x:'2016-12-25', y:20}, {x:'2016-12-26', y:10}]
```
+You can also specify the dataset for a bar chart as arrays of two numbers. This will force rendering of bars with gaps between them (floating-bars). First and second numbers in array will correspond the start and the end point of a bar respectively.
+```javascript
+data: [[5,6], [-3,-6]]
+```
+
## Stacked Bar Chart
Bar charts can be configured into stacked bar charts by changing the settings on the X and Y axes to enable stacking. Stacked bar charts can be used to show how one data series is made up of a number of smaller pieces.
label: me.chart.data.labels[index]
};
+ if (helpers.isArray(dataset.data[index])) {
+ rectangle._model.borderSkipped = null;
+ }
+
me._updateElementGeometry(rectangle, index, reset);
rectangle.pivot();
var scale = me._getValueScale();
var isHorizontal = scale.isHorizontal();
var datasets = chart.data.datasets;
- var value = +scale.getRightValue(datasets[datasetIndex].data[index]);
+ var value = scale._parseValue(datasets[datasetIndex].data[index]);
var minBarLength = scale.options.minBarLength;
var stacked = scale.options.stacked;
var stack = meta.stack;
- var start = 0;
- var i, imeta, ivalue, base, head, size;
+ var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;
+ var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;
+ var i, imeta, ivalue, base, head, size, stackLength;
if (stacked || (stacked === undefined && stack !== undefined)) {
for (i = 0; i < datasetIndex; ++i) {
imeta.controller._getValueScaleId() === scale.id &&
chart.isDatasetVisible(i)) {
- ivalue = +scale.getRightValue(datasets[i].data[index]);
- if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
+ stackLength = scale._parseValue(datasets[i].data[index]);
+ ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;
+
+ if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {
start += ivalue;
}
}
}
base = scale.getPixelForValue(start);
- head = scale.getPixelForValue(start + value);
+ head = scale.getPixelForValue(start + length);
size = head - base;
if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
size = minBarLength;
- if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) {
+ if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) {
head = base - minBarLength;
} else {
head = base + minBarLength;
helpers.canvas.clipArea(chart.ctx, chart.chartArea);
for (; i < ilen; ++i) {
- if (!isNaN(scale.getRightValue(dataset.data[i]))) {
+ var val = scale._parseValue(dataset.data[i]);
+ if (!isNaN(val.min) && !isNaN(val.max)) {
rects[i].draw();
}
}
if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) {
return NaN;
}
+
// If it is in fact an object, dive in one more level
if (rawValue) {
if (this.isHorizontal()) {
return rawValue;
},
+ /**
+ * @private
+ */
+ _parseValue: function(value) {
+ var start, end, min, max;
+
+ if (helpers.isArray(value)) {
+ start = +this.getRightValue(value[0]);
+ end = +this.getRightValue(value[1]);
+ min = Math.min(start, end);
+ max = Math.max(start, end);
+ } else {
+ value = +this.getRightValue(value);
+ start = undefined;
+ end = value;
+ min = value;
+ max = value;
+ }
+
+ return {
+ min: min,
+ max: max,
+ start: start,
+ end: end
+ };
+ },
+
+ /**
+ * @private
+ */
+ _getScaleLabel: function(rawValue) {
+ var v = this._parseValue(rawValue);
+ if (v.start !== undefined) {
+ return '[' + v.start + ', ' + v.end + ']';
+ }
+
+ return +this.getRightValue(rawValue);
+ },
+
/**
* Used to get the value to display in the tooltip for the data at the given index
* @param index
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
helpers.each(dataset.data, function(rawValue, index) {
- var value = +me.getRightValue(rawValue);
- if (isNaN(value) || meta.data[index].hidden) {
+ var value = me._parseValue(rawValue);
+
+ if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden) {
return;
}
positiveValues[index] = positiveValues[index] || 0;
negativeValues[index] = negativeValues[index] || 0;
+ if (value.min === 0 && !opts.ticks.beginAtZero) {
+ value.min = value.max;
+ }
+
if (opts.relativePoints) {
positiveValues[index] = 100;
- } else if (value < 0) {
- negativeValues[index] += value;
+ } else if (value.min < 0 || value.max < 0) {
+ negativeValues[index] += value.min;
} else {
- positiveValues[index] += value;
+ positiveValues[index] += value.max;
}
});
}
var meta = chart.getDatasetMeta(datasetIndex);
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
helpers.each(dataset.data, function(rawValue, index) {
- var value = +me.getRightValue(rawValue);
- if (isNaN(value) || meta.data[index].hidden) {
+ var value = me._parseValue(rawValue);
+
+ if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden) {
return;
}
- if (me.min === null) {
- me.min = value;
- } else if (value < me.min) {
- me.min = value;
+ if (me.min === null || value.min < me.min) {
+ me.min = value.min;
}
- if (me.max === null) {
- me.max = value;
- } else if (value > me.max) {
- me.max = value;
+ if (me.max === null || me.max < value.max) {
+ me.max = value.max;
}
});
}
},
getLabelForIndex: function(index, datasetIndex) {
- return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+ return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
},
// Utils
helpers.each(dataset.data, function(rawValue, index) {
var values = valuesPerStack[key];
- var value = +me.getRightValue(rawValue);
+ var value = me._parseValue(rawValue);
// invalid, hidden and negative values are ignored
- if (isNaN(value) || meta.data[index].hidden || value < 0) {
+ if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden || value.min < 0 || value.max < 0) {
return;
}
values[index] = values[index] || 0;
- values[index] += value;
+ values[index] += value.max;
});
}
});
var meta = chart.getDatasetMeta(datasetIndex);
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
helpers.each(dataset.data, function(rawValue, index) {
- var value = +me.getRightValue(rawValue);
+ var value = me._parseValue(rawValue);
// invalid, hidden and negative values are ignored
- if (isNaN(value) || meta.data[index].hidden || value < 0) {
+ if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden || value.min < 0 || value.max < 0) {
return;
}
- if (me.min === null) {
- me.min = value;
- } else if (value < me.min) {
- me.min = value;
+ if (me.min === null || value.min < me.min) {
+ me.min = value.min;
}
- if (me.max === null) {
- me.max = value;
- } else if (value > me.max) {
- me.max = value;
+ if (me.max === null || me.max < value.max) {
+ me.max = value.max;
}
- if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
- me.minNotZero = value;
+ if (value.min !== 0 && (me.minNotZero === null || value.min < me.minNotZero)) {
+ me.minNotZero = value.min;
}
});
}
// Get the correct tooltip label
getLabelForIndex: function(index, datasetIndex) {
- return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+ return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
},
getPixelForTick: function(index) {
--- /dev/null
+{
+ "config": {
+ "type": "horizontalBar",
+ "data": {
+ "labels": ["2030", "2034", "2038", "2042"],
+ "datasets": [{
+ "backgroundColor": "#FF6384",
+ "data": [11, [6,2], [-4,-7], -2]
+ }, {
+ "backgroundColor": "#36A2EB",
+ "data": [[1,2], [3,4], [-2,-3], [1,4]]
+ }, {
+ "backgroundColor": "#FFCE56",
+ "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+ }]
+ },
+ "options": {
+ "title": false,
+ "legend": false,
+ "scales": {
+ "xAxes": [{
+ "display": false,
+ "ticks": {
+ "min": -8,
+ "max": 12
+ }
+ }],
+ "yAxes": [{
+ "display": false
+ }]
+ }
+ }
+ },
+ "debug": false,
+ "options": {
+ "canvas": {
+ "height": 256,
+ "width": 512
+ }
+ }
+}
--- /dev/null
+{
+ "config": {
+ "type": "horizontalBar",
+ "data": {
+ "labels": ["2030", "2034", "2038", "2042"],
+ "datasets": [{
+ "backgroundColor": "#FF6384",
+ "data": [11, [6,2], [-4,-7], -2]
+ }, {
+ "backgroundColor": "#36A2EB",
+ "data": [[1,2], [3,4], [-2,-3], [1,4]]
+ }, {
+ "backgroundColor": "#FFCE56",
+ "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+ }]
+ },
+ "options": {
+ "title": false,
+ "legend": false,
+ "scales": {
+ "xAxes": [{
+ "display": false,
+ "stacked": true
+ }],
+ "yAxes": [{
+ "display": false,
+ "stacked": true,
+ "ticks": {
+ "min": -8,
+ "max": 12
+ }
+ }]
+ }
+ }
+ },
+ "debug": false,
+ "options": {
+ "canvas": {
+ "height": 256,
+ "width": 512
+ }
+ }
+}
--- /dev/null
+{
+ "config": {
+ "type": "bar",
+ "data": {
+ "labels": ["2030", "2034", "2038", "2042"],
+ "datasets": [{
+ "backgroundColor": "#FF6384",
+ "data": [11, [6,2], [-4,-7], -2]
+ }, {
+ "backgroundColor": "#36A2EB",
+ "data": [[1,2], [3,4], [-2,-3], [1,4]]
+ }, {
+ "backgroundColor": "#FFCE56",
+ "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+ }]
+ },
+ "options": {
+ "title": false,
+ "legend": false,
+ "scales": {
+ "xAxes": [{
+ "display": false,
+ "stacked": true,
+ "ticks": {
+ "min": -8,
+ "max": 12
+ }
+ }],
+ "yAxes": [{
+ "display": false,
+ "stacked": true
+ }]
+ }
+ }
+ },
+ "debug": false,
+ "options": {
+ "canvas": {
+ "height": 256,
+ "width": 512
+ }
+ }
+}
--- /dev/null
+{
+ "config": {
+ "type": "bar",
+ "data": {
+ "labels": ["2030", "2034", "2038", "2042"],
+ "datasets": [{
+ "backgroundColor": "#FF6384",
+ "data": [11, [6,2], [-4,-7], -2]
+ }, {
+ "backgroundColor": "#36A2EB",
+ "data": [[1,2], [3,4], [-2,-3], [1,4]]
+ }, {
+ "backgroundColor": "#FFCE56",
+ "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+ }]
+ },
+ "options": {
+ "title": false,
+ "legend": false,
+ "scales": {
+ "xAxes": [{
+ "display": false,
+ "ticks": {
+ "min": -8,
+ "max": 12
+ }
+ }],
+ "yAxes": [{
+ "display": false
+ }]
+ }
+ }
+ },
+ "debug": false,
+ "options": {
+ "canvas": {
+ "height": 256,
+ "width": 512
+ }
+ }
+}