堆叠,分组的柱形图,其x点的宽度可变

时间:2018-10-30 18:02:50

标签: highcharts

我正在尝试根据具有该x值的非零堆栈的数量动态设置每个x点的宽度。

示例:

我所拥有的: What I have

我想要什么: What I want

我想通过减小每个日期的宽度来消除空白。 (9月7日的宽度应为2,9月8日和9月9日的宽度应为1,而不是所有3个日期的宽度都为3)

这是一个更极端的例子,浪费了很多空间:

Real data example

我研究了各种图表,但似乎找不到包含堆叠和分组的各种图表的示例。 我在stackoverflow上找到的最接近的问题是Display Different Number of Groups in Highcharts Stacked Column Graph,但该答案不会改变点的宽度,只是使条居中。

JSFiddle:http://jsfiddle.net/t3z76o4b/3/

$(function() {
    $('#container').highcharts({
        chart: {
            type: 'column',
        },
        legend: {
            enabled: false
        },
        title: {
            text: 'Fruits by day'
        },
        xAxis: {
            type: 'category',
            labels: {
                rotation: -45,
                formatter: function() {
                    var placeholder = Number(this.value);
                    if (!!placeholder) {
                        return ""
                    }
                    //var obj = data[this.value];
                    if (this.axis.series[0].levelNumber == 1 && !this.isFirst) {
                        return '';
                    } else {
                        return this.value;
                    }
                }
            },
            crosshair: true,
        },
        plotOptions: {
            series: {
                stacking: 'normal'
            }
        },
        series: [{
                name: "Apples",
                stack: "Apples",
                date: "7 Sep 2018",
                data: [{
                        color: "rgba(51,193,59,1)",
                        name: "7 Sep 2018",
                        y: 1
                    },
                    {
                        color: "rgba(51,193,50,0.4)",
                        name: "7 Sep 2018",
                        y: 2
                    }],
            },
            {
                name: "Blueberries",
                stack: "Blueberries",
                date: "7 Sep 2018",
                data: [{
                        color: "rgba(51,50,250,1)",
                        name: "7 Sep 2018",
                        y: 3
                    },
                    {
                        color: "rgba(51,50,250,0.4)",
                        name: "7 Sep 2018",
                        y: 1
                    }],
            },
            {
                name: "Oranges",
                stack: "Oranges",
                date: "8 Sep 2018",
                data: [{
                    color: "rgba(250,193,10,0.5)",
                    name: "8 Sep 2018",
                    y: 1
                }, ],
            },
            {
                name: "Blueberries",
                stack: "Blueberries",
                date: "9 Sep 2018",
                data: [{
                    color: "rgba(51,50,250,1)",
                    name: "9 Sep 2018",
                    y: 2
                }],
            },
        ]
    });
});

在上面的JSFiddle中,每个系列元素代表特定日期的特定水果:

  • series [0] =苹果于2018年9月7日
  • series [1] =蓝莓,2018年9月7日
  • series [2] =橘子,2018年9月8日
  • series [3] = 9月的蓝莓 2018年9月9日

谢谢!

1 个答案:

答案 0 :(得分:1)

不幸的是,Highcharts将组空间计算为xAxis宽度除以类别长度。因此,各个类别之间的空间将始终相等。如上所示,该图表需要对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);
            }

          });
        });
      }
    }
  }

演示: https://jsfiddle.net/wchmiel/9eb74hys/2/