Сгруппированная столбчатая диаграмма с переменной шириной x точек - PullRequest
0 голосов
/ 30 октября 2018

Я пытаюсь динамически установить ширину каждой точки x на основе количества ненулевых стеков с этим значением x.

Пример:

Что у меня есть: What I have

Что я хочу: What I want

Я хочу избавиться от пустых мест, уменьшив ширину каждой даты. (7 сентября должно иметь ширину 2, 8 сентября и 9 сентября должно иметь ширину 1 вместо всех 3 дат, имеющих ширину 3)

Вот более экстремальный пример, много потраченного впустую пространства:

Real data example

Я просматривал диаграммы с переменным масштабом, но, похоже, не могу найти пример диаграммы с широким диапазоном, как с накоплением, так и с группировкой. Самый близкий вопрос, который мне удалось найти в stackoverflow, это Отображать различное количество групп в диаграмме с накоплением столбцов Highcharts , но этот ответ не меняет ширину точек, он просто центрирует столбцы.

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 каждый элемент серии представляет определенный фрукт в определенный день:

  • серия [0] = яблоки 7 сентября 2018
  • серия [1] = Черника 7 сентября 2018
  • серия [2] = Апельсины 8 сентября 2018
  • серия [3] = черника в сентябре 9 2018

Заранее спасибо!

1 Ответ

0 голосов
/ 31 октября 2018

К сожалению, 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/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...