Как включить построение только при наличии значений в Highcharts?

Я новичок в хайчартах. Недавно я застрял в построении высоких чартов. Я использую сюжетные полосы для построения заднего цвета. Если значение присутствует, оно должно быть затенено, если значение отсутствует, оно не должно закрашиваться.

Например, на этой диаграмме для апельсинов затенение хорошо, потому что есть значения как для положительной, так и для отрицательной стороны. А вот для бананов затенение не правильное. Он имеет только положительное значение, поэтому следует закрашивать только положительную сторону, а не отрицательную, т. е. от 0 до -2,5 не следует закрашивать серым цветом.

Любая помощь приветствуется.

Столбец Highchart с графиками:

Столбец Highchart с полосами графика

Код ниже:

Highcharts.chart('container', {  
  chart: {  
    type: 'column'  
  },  
  title: {  
    text: 'Column chart with negative values'  
  },  
  xAxis: {  
    categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas'],  
    plotBands: [{   
      color: 'gray',  
      from: 0.5,  
      to: 1.5  
    },  
    {   
      color: 'gray',  
      from: 3.5,  
      to: 4.5  
    }],  
  },  
  credits: {  
    enabled: false  
  },  
  series: [{  
    name: 'John',  
    data: [5, 3, 4, 7, 2]  
  }, {  
    name: 'Jane',  
    data: [2, -2, -2, 2, 1]  
  }, {  
    name: 'Joe',  
    data: [3, 4, 4, -2, 5]  
  }]  
});  

person Balakumar    schedule 16.08.2018    source источник
comment
Можете ли вы добавить в свой код highcharts?   -  person    schedule 16.08.2018
comment
Привет, добавлен код, и вы также можете увидеть здесь: jsfiddle.net/14a2yb6k/9   -  person Balakumar    schedule 16.08.2018


Ответы (2)


Вы не можете использовать сюжетные полосы для того, что вы описали, так как они идут от -∞ до ∞. Однако вы можете использовать пользовательские формы и сделать их похожими на полосы графика.

Во-первых, вам нужно будет запустить функцию при загрузке диаграммы:

chart: {
  events: {
    load: function() {
      this.customRect = [] //We use this to keep track off all the shapes we create
      addPlotbands(this); //Function to find what area should be colored in what way
    }
  }
}

Затем вам нужна функция для определения, когда все значения положительные, отрицательные или смешанные. Это можно сделать следующим образом (обратите внимание, что предполагается, что все серии имеют точно одинаковое количество точек):

function addPlotbands(chart) {
  let series = chart.series
  let yMin = chart.yAxis[0].getExtremes().min;
  let yMax = chart.yAxis[0].getExtremes().max;

  for (let i = 0; i < series[0].data.length; i++) {
    let allAboveZero = true;
    let allBelowZero = true;
    for (let j = 0; j < chart.series.length; j++) {
      if (series[j].data[i].y >= 0) {
        allBelowZero = false;
      } else {
        allAboveZero = false;
      }
    }
    if (allAboveZero) {
      addCustomElement(chart, i, 0, yMax)
    } else if (allBelowZero) {
      addCustomElement(chart, i, yMin, 0)
    } else {
      addCustomElement(chart, i, yMin, yMax)
    }
  }
}

Затем, наконец, вам нужна функция для создания пользовательской формы прямоугольника, что можно сделать следующим образом:

function addCustomElement(chart, x, yMin, yMax) { //yMin and yMax refers to the window, not the data
  let yAxis = chart.yAxis[0]
  let xAxis = chart.xAxis[0]
  chart.customRect.push(chart.renderer.rect(
    xAxis.toPixels(x - 0.5, false),                                  //Leftmost pixel
    yAxis.toPixels(yMax, false),                                     //Top pixel
    xAxis.toPixels(x + 0.5, false) - xAxis.toPixels(x - 0.5, false), //Width
    yAxis.toPixels(yMin, false) - yAxis.toPixels(yMax, false)        //Height
  )
  .attr({
    'stroke-width': 2,
    fill: 'gray',
    zIndex: -1
  })
  .add());
}

Однако это работает хорошо, когда вы изменяете размер окна, размеры фигур не меняются. Это можно решить, добавив дополнительный код в событие перерисовки:

redraw: function() {
  if (this.customRect) {
    for (let i = 0; i < this.customRect.length; i++) {
      this.customRect[i].destroy(); // Remove all the old rectangles before adding any new ones
    }
    this.customRect = [] //Empty the array of shapes
    let chart = this;
    setTimeout(function() { //We use a small timeout here to make sure the user is done resizing
      addPlotbands(chart)
    }, 100);
  }
}

function addPlotbands(chart) {
  let series = chart.series
  let yMin = chart.yAxis[0].getExtremes().min;
  let yMax = chart.yAxis[0].getExtremes().max;

  for (let i = 0; i < series[0].data.length; i++) {
    let allAboveZero = true;
    let allBelowZero = true;
    for (let j = 0; j < chart.series.length; j++) {
      if (series[j].data[i].y >= 0) {
        allBelowZero = false;
      } else {
        allAboveZero = false;
      }
    }
    if (allAboveZero) {
      addCustomElement(chart, i, 0, yMax)
    } else if (allBelowZero) {
      addCustomElement(chart, i, yMin, 0)
    } else {
      addCustomElement(chart, i, yMin, yMax)
    }
  }
}

function addCustomElement(chart, x, yMin, yMax) {
  let yAxis = chart.yAxis[0]
  let xAxis = chart.xAxis[0]
  chart.customRect.push(chart.renderer.rect(
      xAxis.toPixels(x - 0.5, false), //Leftmost pixel
      yAxis.toPixels(yMax, false), //Topmost pixel
      xAxis.toPixels(x + 0.5, false) - xAxis.toPixels(x - 0.5, false), //Width
      yAxis.toPixels(yMin, false) - yAxis.toPixels(yMax, false) //Height
    )
    .attr({
      'stroke-width': 2,
      fill: 'gray',
      zIndex: -1
    })
    .add());
}

Highcharts.chart('container', {
  chart: {
    type: 'column',
    events: {
      load: function() {
        this.customRect = []
        addPlotbands(this);
      },
      redraw: function() {
        if (this.customRect) {
          for (let i = 0; i < this.customRect.length; i++) {
            this.customRect[i].destroy();
          }
          this.customRect = []
          let chart = this;
          setTimeout(function() {
            addPlotbands(chart)
          }, 100);
        }
      }
    }
  },
  title: {
    text: 'Column chart with negative values'
  },
  xAxis: {
    categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas'],

  },
  credits: {
    enabled: false
  },
  series: [{
    name: 'John',
    data: [5, 3, 4, 7, 2, -3],
  }, {
    name: 'Jane',
    data: [2, -2, -2, 2, 1, -1]
  }, {
    name: 'Joe',
    data: [3, 4, 4, -2, 5, -2]
  }]
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>

Пример работы JSFiddle: https://jsfiddle.net/ewolden/gbrjd2uk/

API REF: Загрузить событие, Событие перерисовки, Добавление прямоугольника

person ewolden    schedule 16.08.2018

Помимо использования plotBands, вы можете создавать (с помощью Highcharts SVGRenderer) белые прямоугольники, закрывающие ненужную часть plotBands. Ниже вы можете найти отзывчивый пример этого решения:

    events: {
        render: function() {
            var chart = this,
                yAxis = chart.yAxis[0],
                xAxis = chart.xAxis[0],
                minPxY = yAxis.toPixels(yAxis.min),
                zeroPxY = yAxis.toPixels(0),
                series = chart.series,
                i,
                j,
                xValue,
                startCategory,
                endCategory,
                hasNegative;

            if (chart.correctedPlotBand) {
                Highcharts.each(chart.correctedPlotBand, function(el) {

                    el.destroy();
                });
                chart.correctedPlotBand = [];
            } else {
                chart.correctedPlotBand = [];
            }

            for (i = 0; i < series[0].points.length; i++) {
                hasNegative = false;

                for (j = 0; j < series.length; j++) {

                    if (series[j].points[i].y < 0) {
                        hasNegative = true;
                    }
                }
                if (!hasNegative) {
                    xValue = series[0].points[i].x;
                    startCategory = xAxis.toPixels(xValue - 0.5) - 1;
                    endCategory = Math.ceil(xAxis.toPixels(xValue + 0.5));

                    chart.correctedPlotBand.push(chart.renderer.rect(startCategory, zeroPxY, endCategory - startCategory, minPxY - zeroPxY)
                        .attr({
                            fill: '#fff',
                            zIndex: 0
                        })
                        .add())
                }
            }

        }
    }

Живая демонстрация: https://jsfiddle.net/BlackLabel/sm1kev4t/

Справочник по API: https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#rect

person ppotaczek    schedule 16.08.2018