[Chartjs]-ChartJS: Position labels at end of doughnut segment

2👍

Your code in the align function really helped me get started but I wanted the positioning to be a bit different and I wrote this code to calculate the position based on the segment amounts, the desired font size (for proper offset), and the width of the canvas’ parent.

let numSectors = insert length of data array here;
let sectorDegree = 180 / numSectors;
let width = (canvas.parent().width() - parseInt(canvas.css('padding').replace("px", "") * 2));
let fontSize = 16;
let b = (width / 2) - 3;
let c = b - (fontSize * 1.8);
let a = Math.sqrt((Math.pow(b, 2) + Math.pow(c, 2)) - (2 * b * c * Math.cos((sectorDegree / 2) * Math.PI / 180)));
let offset = a - (fontSize * 1.2);
let alignAngle = (Math.asin(Math.sin((sectorDegree / 2) * Math.PI / 180) * c / a) * 180 / Math.PI) - (sectorDegree / 2);
datalabels: {
  display: true,
  anchor: 'end',
  font: {
    size: fontSize,
  },
  offset: offset,
  align: function(context) {
    return (sectorDegree * context.dataIndex) - alignAngle;
  },
}

0👍

My idea was to draw an additional transparent chart on top of the original chart that will be in charge of drawing the labels.
The labels chart data will contain segments wrapping the end of each segment in the original chart, in a way that label displayed in the middle of the labels chart segment will actually be displayed at the end of the original chart segments.

Here is the code for drawing the transparent labels chart:


const getLabelsChartData = (min, data) => {
  if (!data) return [];
  const max = data[data.length - 1];
  const step = (max - min) / 1000;
  const values = data
    .slice(0, -1) // remove "max" value
    .map((value, index) => {
      // remove too close values (because labels collapse)
      const prevValue = data[index - 1];
      if (!prevValue || value - prevValue > step * 50) {
        return value;
      }
    })
    .reduce((arr, value) => {
      // create small ranges between each value
      if (value) {
        return [...arr, value - step, value + step];
      }
      return arr;
    }, []);
  return [...values, max];
};

const getLabel = (originalData, labelIndex) => {
  if (labelIndex % 2) {
    const originalDataIndex = Math.floor(labelIndex / 2);
    return originalData[originalDataIndex];
  }
  return '';
};

const labelsChartConfig = {
    type: 'gauge',
    plugins: [ChartDataLabels],
    data: {
      data: getLabelsChartData(minValue, originalData),
      minValue: minValue,
      fill: false,
      backgroundColor: ['transparent'],
      borderColor: 'transparent',
    }
    options: {
      plugins: {
        datalabels: {
          display: true,
          formatter: function (value, context) {
            return getLabel(originalData, context.dataIndex);
          }
          anchor: 'end',
          align: 'end',
        },
      },
    }
}

new Chart(labelsCanvas, labelsChartConfig);

Leave a comment