How can I change the width of the bars in a highchart?

后端 未结 4 499
深忆病人
深忆病人 2020-12-17 02:37

With a bar chart like this one, is is possible to change the width of the bars to represent another data attribute, say the weight of the fruits. The heavier the fruit is, t

4条回答
  •  醉酒成梦
    2020-12-17 02:45

    If I got it right you want every single bar to be of different width. I had same problem and struggled a lot to find a library offering this option. I came to the conclusion - there's none.

    Anyways, I played with highcharts a little, got creative and came up with this:
    You mentioned that you'd like your data to look something like this: data: [[5, 3.4], [3, 6], [4, 3.4]], with the first value being the height and the second being the width.

    Let's do it using the highcharts' column graph.

    Step 1:
    To better differentiate the bars, input each bar as a new series. Since I generated my data dynamically, I had to assign new series dynamically:

    const objects: any = [];
    const extra = this.data.length - 1;
    this.data.map((range) => {
      const obj = {
        type: 'column',
        showInLegend: false,
        data: [range[1]],
        animation: true,
        borderColor: 'black',
        borderWidth: 1,
        color: 'blue'
      };
      for (let i = 0; i < extra; i++) {
        obj.data.push(null);
      }
      objects.push(obj);
    });
    this.chartOptions.series = objects;
    

    That way your different series would look something like this:

    series: [{
      type: 'column',
      data: [5, 3.4]
    }, {
      type: 'column',
      data: [3, 6]
    }, {
      type: 'column',
      data: [4, 3.4]
    }]
    



    Step 2:
    Assign this as plot options for highcharts:

    plotOptions: {
      column: {
        pointPadding: 0,
          borderWidth: 0,
          groupPadding: 0,
          shadow: false
      }
    }
    



    Step 3:
    Now let's get creative - to have the same starting point for all bars, we need to move every single one to the graph's start:

    setColumnsToZero() {
      this.data.map((item, index) => {
        document.querySelector('.highcharts-series-' + index).children[0].setAttribute('x', '0');
      });
    }
    



    Step 4:

    getDistribution() {
      let total = 0;
    
      // Array including all of the bar's data: [[5, 3.4], [3, 6], [4, 3.4]]
      this.data.map(item => {
        total = total + item[0];
      });
    
      // MARK: Get xAxis' total width
      const totalWidth = document.querySelector('.highcharts-axis-line').getBoundingClientRect().width;
    
      let pos = 0;
      this.data.map((item, index) => {
        const start = item[0];
        const width = (start * totalWidth) / total;
        document.querySelector('.highcharts-series-' + index).children[0].setAttribute('width', width.toString());
        document.querySelector('.highcharts-series-' + index).children[0].setAttribute('x', pos.toString());
        pos = pos + width;
        this.getPointsPosition(index, totalWidth, total);
      });
    }
    



    Step 4:
    Let's get to the xAxis' points. In the first functions modify the already existing points, move the last point to the end of the axis and hide the others. In the second function we clone the last point, modify it to have either 6 or 3 total xAxis points and move each of them to the correct position

    getPointsPosition(index, totalWidth, total) {
      const col = document.querySelector('.highcharts-series-' + index).children[0];
      const point = (document.querySelector('.highcharts-xaxis-labels').children[index] as HTMLElement);
      const difference = col.getBoundingClientRect().right - point.getBoundingClientRect().right;
      const half = point.getBoundingClientRect().width / 2;
      if (index === this.data.length - 1) {
        this.cloneNode(point, difference, totalWidth, total);
      } else {
        point.style.display = 'none';
      }
      point.style.transform = 'translateX(' + (+difference + +half) + 'px)';
    
      point.innerHTML = total.toString();
    }
    
    cloneNode(ref: HTMLElement, difference, totalWidth, total) {
      const width = document.documentElement.getBoundingClientRect().width;
    
      const q = total / (width > 1000 && ? 6 : 3);
      const w = totalWidth / (width > 1000 ? 6 : 3);
      let val = total;
      let valW = 0;
      for (let i = 0; i < (width > 1000 ? 6 : 3); i++) {
        val = val - q;
        valW = valW + w;
        const clone = (ref.cloneNode(true) as HTMLElement);
    
        document.querySelector('.highcharts-xaxis-labels').appendChild(clone);
    
        const half = clone.getBoundingClientRect().width / 2;
    
        clone.style.transform = 'translateX(' + (-valW + difference + half) + 'px)';
        const inner = Math.round(val * 100) / 100;
        clone.innerHTML = inner.toString();
      }
    }
    



    In the end we have a graph looking something like this (not the data from this given example, but for [[20, 0.005], [30, 0.013333333333333334], [20, 0.01], [30, 0.005555555555555555], [20, 0.006666666666666666]] with the first value being the width and the second being the height):


    There might be some modifications to do to 100% fit your case. F.e. I had to adjust the xAxis' points a specific starting and end point - I spared this part.

提交回复
热议问题