Highcharts x点宽度可变的堆叠、分组柱状图
我试图根据具有该x值的非零堆栈的数量动态设置每个x点的宽度 例如: 我所拥有的: 我想要的是: 我想通过降低每个日期的宽度来消除空白。9月7日应该有宽度2,9月8日和9月9日应该有宽度1,而不是所有3个日期都有宽度3 这里有一个更极端的例子,大量浪费的空间: 我研究过variwide图表,但似乎找不到同时具有堆叠和分组功能的variwide图表示例。 我能在stackoverflow上找到的最接近的问题是,这个答案不会改变点的宽度,它只是将条居中 JSFiddle: 在上面的JSFIDLE中,每个系列元素表示某一天的某个水果: 系列[0]=2018年9月7日的苹果 系列[1]=2018年9月7日的蓝莓 系列[2]=2018年9月8日的橙子 系列[3]=九月的蓝莓 9 2018Highcharts x点宽度可变的堆叠、分组柱状图,highcharts,Highcharts,我试图根据具有该x值的非零堆栈的数量动态设置每个x点的宽度 例如: 我所拥有的: 我想要的是: 我想通过降低每个日期的宽度来消除空白。9月7日应该有宽度2,9月8日和9月9日应该有宽度1,而不是所有3个日期都有宽度3 这里有一个更极端的例子,大量浪费的空间: 我研究过variwide图表,但似乎找不到同时具有堆叠和分组功能的variwide图表示例。 我能在stackoverflow上找到的最接近的问题是,这个答案不会改变点的宽度,它只是将条居中 JSFiddle: 在上面的JSFIDLE中
提前谢谢 不幸的是,Highcharts将组空间计算为X轴宽度除以类别长度。所以这个空间在不同的类别中总是相等的。如上所示的图表需要对核心Highcharts函数进行大量更改,这很难实现 只有使用一点自定义代码才能对组中的列进行居中:
var stacks = [
'section 1',
'section 2',
'section 3',
'section 4',
'section 5',
'section 6',
'section 7'
];
var categoriesStacksColl = [];
var seriesStackColl = {};
function calculateColumnTranslate(params) {
var m = params.stackLen / 2 + 0.5, // middle bar + offset to calc
barIndex = params.stackIndex,
a = Math.abs(barIndex - m), // bar offset from the middle point
barW = params.barW,
p = params.padding,
posX,
translateX;
if (barIndex === m) {
posX = -barW / 2;
} else if (barIndex > m) {
posX = a * barW + a * p - barW / 2;
} else {
posX = -a * barW - a * p - barW / 2;
}
translateX = posX - params.offset;
return translateX;
}
// Inside Highcharts options
chart: {
type: 'column',
events: {
load: function() {
var chart = this,
series = chart.series,
categoriesLen = chart.xAxis[0].tickPositions.length,
changeWidthFlag = false,
seriesPoints,
nextSeriesPoints,
stack,
length,
arrIndex,
i,
j;
categoriesStacksColl = [];
// Init stacks per categories array
for (j = 0; j < categoriesLen; j++) {
categoriesStacksColl.push(stacks.slice());
}
series.forEach(function(singleSeries) {
stack = singleSeries.options.stack;
if (!seriesStackColl[stack]) {
seriesStackColl[stack] = [];
}
seriesStackColl[stack].push(singleSeries);
});
stacks.forEach(function(initStack) {
seriesPoints = seriesStackColl[initStack][0].points;
length = seriesStackColl[initStack].length;
seriesPoints.forEach(function(point, index) {
if (!point.y && length === 1) {
// increase column width
changeWidthFlag = true;
} else if (!point.y && length > 1) {
changeWidthFlag = true;
for (i = 1; i < length; i++) {
nextSeriesPoints = seriesStackColl[initStack][i].points;
if (nextSeriesPoints[index].y) {
changeWidthFlag = false;
}
}
}
// when all points in category stack are null
if (changeWidthFlag) {
arrIndex = categoriesStacksColl[index].indexOf(initStack);
categoriesStacksColl[index].splice(arrIndex, 1);
changeWidthFlag = false;
}
});
});
},
render: function() {
var chart = this,
series = chart.series[0],
columnMetrics = series.columnMetrics,
barW = columnMetrics.width,
barOffsets = {},
offsets = [],
columnsToTranslate = [],
offsetMin = 0,
offsetMax = 0,
columnsGroupLen = stacks.length,
offset,
columnsGroupWidth,
padding,
point,
pointOffset,
stackIndex,
stackLen,
pointOffsetTemp,
translateBarX;
stacks.forEach(function(stack) {
if (seriesStackColl[stack][0].visible) {
offset = seriesStackColl[stack][0].columnMetrics.offset;
offsetMax = offsetMax < offset ? offset : offsetMax;
offsetMin = offsetMin > offset ? offset : offsetMin;
barOffsets[stack] = offset;
offsets.push(offset);
}
});
columnsGroupWidth = Math.abs(offsetMin) + Math.abs(offsetMax) + barW;
padding = (columnsGroupWidth - columnsGroupLen * barW) / (columnsGroupLen - 1);
categoriesStacksColl.forEach(function(cat, index) {
if (cat.length < stacks.length) {
columnsToTranslate.push({
index: index,
stack: cat
});
}
});
columnsToTranslate.forEach(function(elem) {
stackIndex = 0;
pointOffsetTemp = 0;
chart.series.forEach(function(singleSeries) {
point = singleSeries.points[elem.index];
if (point.y && singleSeries.visible) {
pointOffset = point.series.columnMetrics.offset;
stackLen = elem.stack.length;
if (pointOffsetTemp !== pointOffset) {
pointOffsetTemp = pointOffset;
stackIndex++;
}
translateBarX = calculateColumnTranslate({
padding: padding,
barW: barW,
offset: pointOffset,
stackIndex: stackIndex,
stackLen: stackLen
});
point.graphic.translate(translateBarX);
}
});
});
}
}
}
演示:
var stacks = [
'section 1',
'section 2',
'section 3',
'section 4',
'section 5',
'section 6',
'section 7'
];
var categoriesStacksColl = [];
var seriesStackColl = {};
function calculateColumnTranslate(params) {
var m = params.stackLen / 2 + 0.5, // middle bar + offset to calc
barIndex = params.stackIndex,
a = Math.abs(barIndex - m), // bar offset from the middle point
barW = params.barW,
p = params.padding,
posX,
translateX;
if (barIndex === m) {
posX = -barW / 2;
} else if (barIndex > m) {
posX = a * barW + a * p - barW / 2;
} else {
posX = -a * barW - a * p - barW / 2;
}
translateX = posX - params.offset;
return translateX;
}
// Inside Highcharts options
chart: {
type: 'column',
events: {
load: function() {
var chart = this,
series = chart.series,
categoriesLen = chart.xAxis[0].tickPositions.length,
changeWidthFlag = false,
seriesPoints,
nextSeriesPoints,
stack,
length,
arrIndex,
i,
j;
categoriesStacksColl = [];
// Init stacks per categories array
for (j = 0; j < categoriesLen; j++) {
categoriesStacksColl.push(stacks.slice());
}
series.forEach(function(singleSeries) {
stack = singleSeries.options.stack;
if (!seriesStackColl[stack]) {
seriesStackColl[stack] = [];
}
seriesStackColl[stack].push(singleSeries);
});
stacks.forEach(function(initStack) {
seriesPoints = seriesStackColl[initStack][0].points;
length = seriesStackColl[initStack].length;
seriesPoints.forEach(function(point, index) {
if (!point.y && length === 1) {
// increase column width
changeWidthFlag = true;
} else if (!point.y && length > 1) {
changeWidthFlag = true;
for (i = 1; i < length; i++) {
nextSeriesPoints = seriesStackColl[initStack][i].points;
if (nextSeriesPoints[index].y) {
changeWidthFlag = false;
}
}
}
// when all points in category stack are null
if (changeWidthFlag) {
arrIndex = categoriesStacksColl[index].indexOf(initStack);
categoriesStacksColl[index].splice(arrIndex, 1);
changeWidthFlag = false;
}
});
});
},
render: function() {
var chart = this,
series = chart.series[0],
columnMetrics = series.columnMetrics,
barW = columnMetrics.width,
barOffsets = {},
offsets = [],
columnsToTranslate = [],
offsetMin = 0,
offsetMax = 0,
columnsGroupLen = stacks.length,
offset,
columnsGroupWidth,
padding,
point,
pointOffset,
stackIndex,
stackLen,
pointOffsetTemp,
translateBarX;
stacks.forEach(function(stack) {
if (seriesStackColl[stack][0].visible) {
offset = seriesStackColl[stack][0].columnMetrics.offset;
offsetMax = offsetMax < offset ? offset : offsetMax;
offsetMin = offsetMin > offset ? offset : offsetMin;
barOffsets[stack] = offset;
offsets.push(offset);
}
});
columnsGroupWidth = Math.abs(offsetMin) + Math.abs(offsetMax) + barW;
padding = (columnsGroupWidth - columnsGroupLen * barW) / (columnsGroupLen - 1);
categoriesStacksColl.forEach(function(cat, index) {
if (cat.length < stacks.length) {
columnsToTranslate.push({
index: index,
stack: cat
});
}
});
columnsToTranslate.forEach(function(elem) {
stackIndex = 0;
pointOffsetTemp = 0;
chart.series.forEach(function(singleSeries) {
point = singleSeries.points[elem.index];
if (point.y && singleSeries.visible) {
pointOffset = point.series.columnMetrics.offset;
stackLen = elem.stack.length;
if (pointOffsetTemp !== pointOffset) {
pointOffsetTemp = pointOffset;
stackIndex++;
}
translateBarX = calculateColumnTranslate({
padding: padding,
barW: barW,
offset: pointOffset,
stackIndex: stackIndex,
stackLen: stackLen
});
point.graphic.translate(translateBarX);
}
});
});
}
}
}