I am open to learning that there is already a way (via configuration, or developing a plugin) to hook into the rendering of the label of an axis, such that I could control aspects of the font used to render each line of a multiline label (e.g., what I need to render would be similar visually to a label and sub-label below it, with the primary label being bolded and a larger font size, while the sub-label directly beneath it would be normal font weight and a smaller size).
I am using ChartJs version 3.5.1 to render a horizontal barchart (meaning that the dataset labels on the left are really configured under the y axis), and have tried a few different things already:
- Hooking into the tick callback - but I can't even use this function to duplicate default functionality (the value coming into that function isn't the label text; instead it is the index/ordinal of the data row?). Even if I could get this to work as shown in examples, it appears like this would be more for the content of the label than any of the configuration options themselves.
- Setting the font configuration for
ticks
to be an array - but this only serves to allow me to change the font between data rows (e.g., I can make the label of the top row in my horizontal bar chart be size 22, the second label 10, etc. - but not change font attributes within lines of a given label) - Using a plugin like
afterDraw
to try to go tweak things - but again, the configuration at that point seems to only consider all of the lines together as one label. - Tried looking through past PRs to the project (mostly centered around adding multiline label support, as well as bug fixes specific to that area) to get any additional insight
If there isn't a way currently (via plugins or existing configuration), does anyone have a good feel for where to start attacking this sort of a change as a new PR?
UPDATE As was shared as a response to my corresponding ChartJs feature request and as the accepted answer below, a custom plugin seems to be the only way currently to accomplish what I wanted for now.
Here are the key bits from my configuration (admittedly much more "one time use only" than the accepted answer, as I moved some of the configuration inside of the plugin as hard-coded values given my relatively narrow use case):
// this will be passed into the chart constructor...
const options = {
//...
scales: {
//...
// I wanted to impact the lefthand side of a horizontal bar chart
y: {
ticks: {
// make the original labels white for later painting over with custom sub-labels
color: "white",
// we still want this here to be able to take up the same space as the eventual label we will stick here
font: {
size: 22,
weight: "bold"
}
}
},
//...
}
};
// This is my plugin, also later passed into the chart constructor
const customSubLabelsPlugin = {
id: "customSubLabels",
afterDraw: (chart, args, opts) => {
// Set all variables needed
const {
ctx,
// I only cared about altering one specific axis
scales: { y }
} = chart;
const labelItems = y._labelItems;
const fontStringSubTitle = "16px Helvetica,Arial,sans-serif";
const fontStringMain = "bold 22px Helvetica,Arial,sans-serif";
// loop over each dataset label
for (let i = 0; i < labelItems.length; i++) {
let labelItem = labelItems[i];
// For purposes of redrawing, we are going to always assume that each label is an array - because we make it that way if we need to
const label = Array.isArray(labelItem.label)
? labelItem.label
: [labelItem.label];
// Draw new text on canvas
let offset = 0;
label.forEach((el) => {
let elTextMetrics = ctx.measureText(el);
if (labelItem.label.indexOf(el) === 0) {
ctx.font = fontStringMain;
} else {
ctx.font = fontStringSubTitle;
}
ctx.save();
ctx.fillStyle = "#546a6f";
ctx.fillText(
el,
labelItem.translation[0],
labelItem.translation[1] + labelItem.textOffset + offset
);
ctx.restore();
offset +=
elTextMetrics.fontBoundingBoxAscent +
elTextMetrics.fontBoundingBoxDescent;
});
}
}
};