ChartJs:有没有办法控制多行轴标签的每行字体选项

ChartJs: Is there a way to control the font options per line for a multiline axis label

我乐于了解已经有一种方法(通过配置或开发插件)挂钩轴标签的渲染,这样我就可以控制用于渲染每个轴的字体的各个方面多行标签的行(例如,我需要呈现的内容在视觉上与其下方的标签和子标签相似,主标签加粗且字体较大,而其正下方的子标签是正常的字体粗细和较小的尺寸)。

我正在使用 ChartJs 3.5.1 版来呈现水平条形图(这意味着左侧的数据集标签实际上是在 y 轴下配置的),并且已经尝试了一些不同的方法:

如果目前没有办法(通过插件或现有配置),是否有人知道从哪里开始攻击这种作为新 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>