答案 0 :(得分:15)
这是Chart.js 2.1的一部分吧!图表类型为horizontalBar
(小写h)。
var config = {
type: 'horizontalBar',
data: {
...
小提琴 - http://jsfiddle.net/gpxjohup/
原始答案
预览强>
<强>脚本强>
此代码的大部分是Chart.js库中bar
代码的副本,其中x / y和宽度/高度交换。感谢@ZachPanzarino帮助使用轴标签!
Chart.defaults.HorizontalBar = {
hover: {
mode: "single"
},
scales: {
yAxes: [{
position: 'left',
type: "category",
categoryPercentage: 0.8,
barPercentage: 1,
gridLines: { offsetGridLines: true }
}],
xAxes: [{
ticks: {
beginAtZero: true
},
position: 'bottom',
type: "linear",
}],
},
};
Chart.controllers.HorizontalBar = Chart.controllers.bar.extend({
updateElement: function updateElement(rectangle, index, reset, numBars) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScalePoint;
if (xScale.min < 0 && xScale.max < 0) {
xScalePoint = xScale.getPixelForValue(xScale.max);
} else if (xScale.min > 0 && xScale.max > 0) {
xScalePoint = xScale.getPixelForValue(xScale.min);
} else {
xScalePoint = xScale.getPixelForValue(0);
}
Chart.helpers.extend(rectangle, {
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
_index: index,
_model: {
x: reset ? xScalePoint : this.calculateBarX(index, this.index),
y: this.calculateBarY(index, this.index),
label: this.chart.data.labels[index],
datasetLabel: this.getDataset().label,
base: this.calculateBarBase(this.index, index),
height: this.calculateBarHeight(numBars),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : Chart.helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : Chart.helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : Chart.helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
},
// override the draw and inRange functions because the one in the library needs width (we only have height)
draw: function() {
var ctx = this._chart.ctx;
ctx.fillStyle = this._view.backgroundColor;
ctx.fillRect(this._view.base, this._view.y - this._view.height / 2, this._view.x - this._view.base, this._view.height);
ctx.strokeStyle = this._view.borderColor;
ctx.strokeWidth = this._view.borderWidth;
ctx.strokeRect(this._view.base, this._view.y - this._view.height / 2, this._view.x - this._view.base, this._view.height);
},
inRange: function (mouseX, mouseY) {
var vm = this._view;
var inRange = false;
if (vm) {
if (vm.x < vm.base) {
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base);
} else {
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x);
}
}
return inRange;
}
});
rectangle.pivot();
// the animation progresses _view values from their current value to the _model value
rectangle._view.x = rectangle._model.base;
},
calculateBarBase: function (datasetIndex, index) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var base = 0;
if (xScale.options.stacked) {
var value = this.chart.data.datasets[datasetIndex].data[index];
if (value < 0) {
for (var i = 0; i < datasetIndex; i++) {
var negDS = this.chart.data.datasets[i];
if (Chart.helpers.isDatasetVisible(negDS) && negDS.xAxisID === xScale.id) {
base += negDS.data[index] < 0 ? negDS.data[index] : 0;
}
}
} else {
for (var j = 0; j < datasetIndex; j++) {
var posDS = this.chart.data.datasets[j];
if (Chart.helpers.isDatasetVisible(posDS) && posDS.xAxisID === xScale.id) {
base += posDS.data[index] > 0 ? posDS.data[index] : 0;
}
}
}
return xScale.getPixelForValue(base);
}
base = xScale.getPixelForValue(xScale.min);
if (xScale.beginAtZero || ((xScale.min <= 0 && xScale.max >= 0) || (xScale.min >= 0 && xScale.max <= 0))) {
base = xScale.getPixelForValue(0, 0);
} else if (xScale.min < 0 && xScale.max < 0) {
base = xScale.getPixelForValue(xScale.max);
}
return base;
},
getRuler: function () {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var datasetCount = this.getBarCount();
var tickHeight = (function () {
var min = yScale.getPixelForTick(1) - yScale.getPixelForTick(0);
for (var i = 2; i < this.getDataset().data.length; i++) {
min = Math.min(yScale.getPixelForTick(i) - yScale.getPixelForTick(i - 1), min);
}
return min;
}).call(this);
var categoryHeight = tickHeight * yScale.options.categoryPercentage;
var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
var fullBarHeight = categoryHeight / datasetCount;
var barHeight = fullBarHeight * yScale.options.barPercentage;
var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);
return {
datasetCount: datasetCount,
tickHeight: tickHeight,
categoryHeight: categoryHeight,
categorySpacing: categorySpacing,
fullBarHeight: fullBarHeight,
barHeight: barHeight,
barSpacing: barSpacing,
};
},
calculateBarHeight: function () {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var ruler = this.getRuler();
if (yScale.options.stacked) {
return ruler.categoryHeight;
}
return ruler.barHeight;
},
calculateBarY: function (index, datasetIndex) {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var barIndex = this.getBarIndex(datasetIndex);
var ruler = this.getRuler();
var leftTick = yScale.getPixelForValue(null, index, datasetIndex, this.chart.isCombo);
leftTick -= this.chart.isCombo ? (ruler.tickHeight / 2) : 0;
if (yScale.options.stacked) {
return leftTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
}
return leftTick +
(ruler.barHeight / 2) +
ruler.categorySpacing +
(ruler.barHeight * barIndex) +
(ruler.barSpacing / 2) +
(ruler.barSpacing * barIndex);
},
calculateBarX: function (index, datasetIndex) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var value = this.getDataset().data[index];
if (xScale.options.stacked) {
var sumPos = 0,
sumNeg = 0;
for (var i = 0; i < datasetIndex; i++) {
var ds = this.chart.data.datasets[i];
if (Chart.helpers.isDatasetVisible(ds)) {
if (ds.data[index] < 0) {
sumNeg += ds.data[index] || 0;
} else {
sumPos += ds.data[index] || 0;
}
}
}
if (value < 0) {
return xScale.getPixelForValue(sumNeg + value);
} else {
return xScale.getPixelForValue(sumPos + value);
}
return xScale.getPixelForValue(value);
}
return xScale.getPixelForValue(value);
}
});
然后
var config = {
type: 'HorizontalBar',
...
答案 1 :(得分:0)
这是一个暂时的黑客,但现在你可以将整个图形旋转90度,使其水平。
#my-chart {
width: 100%;
transform: rotate(-90deg);
}
.column {
width: 100%;
height: 50px;
}
.column-1 {
background: orange;
}
.column-2 {
background: green;
}
&#13;
<div id="my-chart">
<div class="column column-1">Column 1</div>
<div class="column column-2">Column 2</div>
</div>
&#13;
答案 2 :(得分:0)
我不确定它是否可行,但你可以试试。我现在正在研究Charts.js并发现了一些限制,偶然发现了这个自定义版本的Charts.js库
https://github.com/FVANCOP/ChartNew.js
您无需更改用于提取数据的任何旧格式。 只需替换您要使用的脚本:
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="/pathTo/ChartNew.js"></script>
<script src="/pathTo/shapesInChart.js"></script>
在shapesInChart.js文件中,您将看到自定义的形状和个案。它有一个用于HorizontalBar和HorizontalStackedBar
让我知道它是否适合你:)
答案 3 :(得分:0)
我编辑了@potatopeelings原始示例,所以现在你可以将offsetGridLines设置为你喜欢和工作的工具提示!
Chart.defaults.HorizontalBar = {
hover: {
mode: "label"
},
tooltips: {
mode: "label",
callbacks: {
title: function(tooltipItems, data) {
label = [];
for (index in tooltipItems) {
if (label.indexOf(tooltipItems[index].yLabel) < 0) label.push(tooltipItems[index].yLabel)
}
return label.join(", ");
},
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ': ' + tooltipItem.xLabel;
}
}
},
scales: {
yAxes: [{
position: 'left',
type: "category",
categoryPercentage: 0.8,
barPercentage: 1,
gridLines: {
offsetGridLines: true
}
}],
xAxes: [{
ticks: {
beginAtZero: true
},
position: 'bottom',
type: "linear",
}],
},
};
Chart.controllers.HorizontalBar = Chart.controllers.bar.extend({
initialize: function(chart, datasetIndex) {
// insert dummy labels for proper vertical positioning
if (!datasetIndex && !chart.config.options.scales.yAxes[0].gridLines.offsetGridLines) {
chart.config.data.labels.unshift('')
chart.config.data.labels.push('')
}
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
this.getDataset().bar = true;
},
updateElement: function updateElement(rectangle, index, reset, numBars) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScalePoint;
if (xScale.min < 0 && xScale.max < 0) {
xScalePoint = xScale.getPixelForValue(xScale.max);
} else if (xScale.min > 0 && xScale.max > 0) {
xScalePoint = xScale.getPixelForValue(xScale.min);
} else {
xScalePoint = xScale.getPixelForValue(0);
}
Chart.helpers.extend(rectangle, {
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
_index: index,
_model: {
x: reset ? xScalePoint : this.calculateBarX(index, this.index),
y: this.calculateBarY(index, this.index),
label: this.chart.data.labels[index],
datasetLabel: this.getDataset().label,
base: this.calculateBarBase(this.index, index),
height: this.calculateBarHeight(numBars),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : Chart.helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : Chart.helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : Chart.helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
},
// override the inRange function because the one in the library needs width (we have only height)
inRange: function(mouseX, mouseY) {
var vm = this._view;
var inRange = false;
if (vm) {
if (vm.x < vm.base) {
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base);
} else {
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x);
}
}
return inRange;
}
});
rectangle.pivot();
// the animation progresses _view values from their current value to the _model value
rectangle._view.x = rectangle._model.base;
},
calculateBarBase: function(datasetIndex, index) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var base = 0;
if (xScale.options.stacked) {
var value = this.chart.data.datasets[datasetIndex].data[index];
if (value < 0) {
for (var i = 0; i < datasetIndex; i++) {
var negDS = this.chart.data.datasets[i];
if (Chart.helpers.isDatasetVisible(negDS) && negDS.xAxisID === xScale.id) {
base += negDS.data[index] < 0 ? negDS.data[index] : 0;
}
}
} else {
for (var j = 0; j < datasetIndex; j++) {
var posDS = this.chart.data.datasets[j];
if (Chart.helpers.isDatasetVisible(posDS) && posDS.xAxisID === xScale.id) {
base += posDS.data[index] > 0 ? posDS.data[index] : 0;
}
}
}
return xScale.getPixelForValue(base);
}
base = xScale.getPixelForValue(xScale.min);
if (xScale.beginAtZero || ((xScale.min <= 0 && xScale.max >= 0) || (xScale.min >= 0 && xScale.max <= 0))) {
base = xScale.getPixelForValue(0, 0);
} else if (xScale.min < 0 && xScale.max < 0) {
base = xScale.getPixelForValue(xScale.max);
}
return base;
},
draw: function(ease) {
var easingDecimal = ease || 1;
var ctx = this.chart.chart.ctx;
ctx.save();
Chart.helpers.each(this.getDataset().metaData, function(rectangle, index) {
ctx.fillStyle = rectangle._view.backgroundColor;
ctx.fillRect(rectangle._view.base, rectangle._view.y - rectangle._view.height / 2, rectangle._view.x - rectangle._view.base, rectangle._view.height);
ctx.strokeStyle = rectangle._view.borderColor;
ctx.strokeWidth = rectangle._view.borderWidth;
ctx.strokeRect(rectangle._view.base, rectangle._view.y - rectangle._view.height / 2, rectangle._view.x - rectangle._view.base, rectangle._view.height);
rectangle.transition(easingDecimal).draw();
}, this);
ctx.restore();
},
getRuler: function() {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var datasetCount = this.getBarCount();
var tickHeight = (function() {
var min = yScale.getPixelForTick(1) - yScale.getPixelForTick(0);
for (var i = 2; i < this.getDataset().data.length; i++) {
min = Math.min(yScale.getPixelForTick(i) - yScale.getPixelForTick(i - 1), min);
}
return min;
}).call(this);
var categoryHeight = tickHeight * yScale.options.categoryPercentage;
var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
var fullBarHeight = categoryHeight / datasetCount;
var barHeight = fullBarHeight * yScale.options.barPercentage;
var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);
return {
datasetCount: datasetCount,
tickHeight: tickHeight,
categoryHeight: categoryHeight,
categorySpacing: categorySpacing,
fullBarHeight: fullBarHeight,
barHeight: barHeight,
barSpacing: barSpacing,
};
},
calculateBarHeight: function() {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var ruler = this.getRuler();
console.debug(yScale.options.stacked);
if (yScale.options.stacked) {
return ruler.categoryHeight;
}
return ruler.barHeight;
},
calculateBarY: function(index, datasetIndex) {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var barIndex = this.getBarIndex(datasetIndex);
var ruler = this.getRuler();
var leftTick = yScale.getPixelForValue(null, index, datasetIndex, this.chart.isCombo);
leftTick -= this.chart.isCombo ? (ruler.tickHeight / 2) : 0;
leftTick = (!yScale.options.gridLines.offsetGridLines) ? leftTick + (ruler.tickHeight / 2) : leftTick;
if (yScale.options.stacked) {
return leftTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
}
return leftTick + 1 +
(ruler.barHeight / 2) +
ruler.categorySpacing +
(ruler.barHeight * barIndex) +
(ruler.barSpacing / 2) +
(ruler.barSpacing * barIndex);
},
calculateBarX: function(index, datasetIndex) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var value = this.getDataset().data[index];
if (xScale.options.stacked) {
var sumPos = 0,
sumNeg = 0;
for (var i = 0; i < datasetIndex; i++) {
var ds = this.chart.data.datasets[i];
if (Chart.helpers.isDatasetVisible(ds)) {
if (ds.data[index] < 0) {
sumNeg += ds.data[index] || 0;
} else {
sumPos += ds.data[index] || 0;
}
}
}
if (value < 0) {
return xScale.getPixelForValue(sumNeg + value);
} else {
return xScale.getPixelForValue(sumPos + value);
}
return xScale.getPixelForValue(value);
}
});
欢呼声 c_bb