ChartJs:有没有办法控制多行轴标签的每行字体选项
ChartJs: Is there a way to control the font options per line for a multiline axis label
我乐于了解已经有一种方法(通过配置或开发插件)挂钩轴标签的渲染,这样我就可以控制用于渲染每个轴的字体的各个方面多行标签的行(例如,我需要呈现的内容在视觉上与其下方的标签和子标签相似,主标签加粗且字体较大,而其正下方的子标签是正常的字体粗细和较小的尺寸)。
我正在使用 ChartJs 3.5.1 版来呈现水平条形图(这意味着左侧的数据集标签实际上是在 y 轴下配置的),并且已经尝试了一些不同的方法:
- 挂钩 tick 回调 - 但我什至不能使用此函数来复制默认功能(进入该函数的值不是标签文本;而是数据的 index/ordinal排?)。即使我能让它像示例中所示那样工作,它看起来更像是标签的内容而不是任何配置选项本身。
- 将
ticks
的字体配置设置为数组 - 但这仅用于允许我更改数据行之间的字体(例如,我可以在我的水平栏中制作顶行的标签图表大小为 22,第二个标签为 10,等等 - 但不更改给定标签行内的字体属性)
- 使用像
afterDraw
这样的插件来尝试调整一些东西 - 但同样,此时的配置似乎只将所有行一起视为一个标签。
- 尝试查看项目的过去 PR(主要围绕添加多行标签支持以及特定于该领域的错误修复)以获得任何额外的见解
如果目前没有办法(通过插件或现有配置),是否有人知道从哪里开始攻击这种作为新 PR 的更改?
更新
作为对我相应 ChartJs feature request 的回应和下面接受的答案所分享的那样,自定义插件似乎是目前完成我现在想要的东西的唯一方法。
这是我的配置中的关键位(诚然,“一次性使用”比接受的答案要多得多,因为鉴于我相对狭窄的用例,我将插件中的一些配置作为硬编码值移动了):
// 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;
});
}
}
};
您可以使用插件为您重新绘制刻度,可能需要根据您的特定需求进行一些微调:
var options = {
type: 'line',
data: {
labels: [
["Red", "subTitle"],
["Blue", "subTitle"],
["Yellow", "subTitle"],
["Green", "subTitle"],
["Purple", "subTitle"],
["Orange", "subTitle"]
],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'red',
backgroundColor: 'red'
}]
},
options: {
plugins: {
customTextColor: {
color: 'blue',
boxColor: 'white',
fontStringSubTitle: 'italic 12px Comic Sans MS',
fontStringMain: ''
}
}
},
plugins: [{
id: 'customTextColor',
afterDraw: (chart, args, opts) => {
// Set all variables needed
const {
ctx,
scales: {
y,
x
}
} = chart;
const labelItems = x._labelItems;
const {
color,
boxColor,
fontStringMain,
fontStringSubTitle
} = opts;
const defaultFontString = '12px "Helvetica Neue", Helvetica, Arial, sans-serif';
for (let i = 0; i < labelItems.length; i++) {
let labelItem = labelItems[i];
if (!Array.isArray(labelItem.label)) {
continue;
}
let metrics = ctx.measureText(labelItem.label);
let labelWidth = metrics.width;
let labelHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
//Draw box over old labels so they are inviseble
ctx.save();
ctx.fillStyle = boxColor || '#FFFFFF';
ctx.fillRect((labelItem.translation[0] - labelWidth / 2), labelItem.translation[1], labelWidth, labelHeight * labelItem.label.length);
ctx.restore();
// Draw new text on canvas
let offset = 0;
labelItem.label.forEach(el => {
let elTextMetrics = ctx.measureText(el);
let elWidth = elTextMetrics.width;
if (labelItem.label.indexOf(el) === 0) {
ctx.font = fontStringMain || defaultFontString;
} else {
ctx.font = fontStringSubTitle || defaultFontString;
}
ctx.save();
ctx.fillStyle = color || Chart.defaults.color
ctx.fillText(el, (labelItem.translation[0] - elWidth / 2), labelItem.translation[1] + labelItem.textOffset + offset);
ctx.restore();
offset += elTextMetrics.fontBoundingBoxAscent + elTextMetrics.fontBoundingBoxDescent;
});
}
// Draw white box over old label
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.js"></script>
</body>
我乐于了解已经有一种方法(通过配置或开发插件)挂钩轴标签的渲染,这样我就可以控制用于渲染每个轴的字体的各个方面多行标签的行(例如,我需要呈现的内容在视觉上与其下方的标签和子标签相似,主标签加粗且字体较大,而其正下方的子标签是正常的字体粗细和较小的尺寸)。
我正在使用 ChartJs 3.5.1 版来呈现水平条形图(这意味着左侧的数据集标签实际上是在 y 轴下配置的),并且已经尝试了一些不同的方法:
- 挂钩 tick 回调 - 但我什至不能使用此函数来复制默认功能(进入该函数的值不是标签文本;而是数据的 index/ordinal排?)。即使我能让它像示例中所示那样工作,它看起来更像是标签的内容而不是任何配置选项本身。
- 将
ticks
的字体配置设置为数组 - 但这仅用于允许我更改数据行之间的字体(例如,我可以在我的水平栏中制作顶行的标签图表大小为 22,第二个标签为 10,等等 - 但不更改给定标签行内的字体属性) - 使用像
afterDraw
这样的插件来尝试调整一些东西 - 但同样,此时的配置似乎只将所有行一起视为一个标签。 - 尝试查看项目的过去 PR(主要围绕添加多行标签支持以及特定于该领域的错误修复)以获得任何额外的见解
如果目前没有办法(通过插件或现有配置),是否有人知道从哪里开始攻击这种作为新 PR 的更改?
更新 作为对我相应 ChartJs feature request 的回应和下面接受的答案所分享的那样,自定义插件似乎是目前完成我现在想要的东西的唯一方法。
这是我的配置中的关键位(诚然,“一次性使用”比接受的答案要多得多,因为鉴于我相对狭窄的用例,我将插件中的一些配置作为硬编码值移动了):
// 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;
});
}
}
};
您可以使用插件为您重新绘制刻度,可能需要根据您的特定需求进行一些微调:
var options = {
type: 'line',
data: {
labels: [
["Red", "subTitle"],
["Blue", "subTitle"],
["Yellow", "subTitle"],
["Green", "subTitle"],
["Purple", "subTitle"],
["Orange", "subTitle"]
],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'red',
backgroundColor: 'red'
}]
},
options: {
plugins: {
customTextColor: {
color: 'blue',
boxColor: 'white',
fontStringSubTitle: 'italic 12px Comic Sans MS',
fontStringMain: ''
}
}
},
plugins: [{
id: 'customTextColor',
afterDraw: (chart, args, opts) => {
// Set all variables needed
const {
ctx,
scales: {
y,
x
}
} = chart;
const labelItems = x._labelItems;
const {
color,
boxColor,
fontStringMain,
fontStringSubTitle
} = opts;
const defaultFontString = '12px "Helvetica Neue", Helvetica, Arial, sans-serif';
for (let i = 0; i < labelItems.length; i++) {
let labelItem = labelItems[i];
if (!Array.isArray(labelItem.label)) {
continue;
}
let metrics = ctx.measureText(labelItem.label);
let labelWidth = metrics.width;
let labelHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
//Draw box over old labels so they are inviseble
ctx.save();
ctx.fillStyle = boxColor || '#FFFFFF';
ctx.fillRect((labelItem.translation[0] - labelWidth / 2), labelItem.translation[1], labelWidth, labelHeight * labelItem.label.length);
ctx.restore();
// Draw new text on canvas
let offset = 0;
labelItem.label.forEach(el => {
let elTextMetrics = ctx.measureText(el);
let elWidth = elTextMetrics.width;
if (labelItem.label.indexOf(el) === 0) {
ctx.font = fontStringMain || defaultFontString;
} else {
ctx.font = fontStringSubTitle || defaultFontString;
}
ctx.save();
ctx.fillStyle = color || Chart.defaults.color
ctx.fillText(el, (labelItem.translation[0] - elWidth / 2), labelItem.translation[1] + labelItem.textOffset + offset);
ctx.restore();
offset += elTextMetrics.fontBoundingBoxAscent + elTextMetrics.fontBoundingBoxDescent;
});
}
// Draw white box over old label
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.js"></script>
</body>