Highcharts x点宽度可变的堆叠、分组柱状图

Highcharts x点宽度可变的堆叠、分组柱状图,highcharts,Highcharts,我试图根据具有该x值的非零堆栈的数量动态设置每个x点的宽度 例如: 我所拥有的: 我想要的是: 我想通过降低每个日期的宽度来消除空白。9月7日应该有宽度2,9月8日和9月9日应该有宽度1,而不是所有3个日期都有宽度3 这里有一个更极端的例子,大量浪费的空间: 我研究过variwide图表,但似乎找不到同时具有堆叠和分组功能的variwide图表示例。 我能在stackoverflow上找到的最接近的问题是,这个答案不会改变点的宽度,它只是将条居中 JSFiddle: 在上面的JSFIDLE中

我试图根据具有该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 2018
提前谢谢

不幸的是,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);
            }

          });
        });
      }
    }
  }