chartjs/Chart.js

Customizeable Axis Title Rendering

Open

#3,214 opened on 2016年8月25日

GitHub で見る
 (4 comments) (0 reactions) (0 assignees)JavaScript (67,416 stars) (11,951 forks)batch import
help wantedtype: enhancement

説明

In order to make 2-axis (opposing axis) charts a bit more readable than this https://jsfiddle.net/tah74jm4/ (which line belongs to which label?), the designer wanted a color indication on the label for each line. I know there are legends, however that takes up additional space and is also not very flexible in positioning the legend items. In this case, a legend next to each label would better indicate which values belong to which line.

One option we were considering was coloring the label in the matching color of the line, however since the colors can be dynamic based on user selection, there could be colors which would make the label text hard to read. So this option was thrown out.

The option chosen was a black label with a dot next to it in the color of the line, as you can see here:

screen shot 2016-08-25 at 1 55 06 pm

This is a screenshot of an actual graph, which I was able to generate by making a slight modification in the draw function in core.scale.js in the scaleLabel.display part:

if (scaleLabel.display) {
  // Draw the scale label
  scaleLabelX = options.position === 'left' ? this.left + (scaleLabelFontSize / 2) : this.right - (scaleLabelFontSize / 2);
  scaleLabelY = this.top + ((this.bottom - this.top) / 2);
  var rotation = options.position === 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;

  // added "if-condition" allows for multi-colored label
  if (scaleLabel.labelString instanceof Array) {
    var x = 0;
    var fullText = '';
    var fullTextOffset;
    var labelOffset;

    context.save();

    helpers.each(scaleLabel.labelString, function (obj) {
      fullText += obj.text;
    });

    fullTextOffset = context.measureText(fullText).width / 2;
    labelOffset = options.position === 'left' ? fullTextOffset : fullTextOffset * -1;

    context.translate(scaleLabelX, scaleLabelY + labelOffset);
    context.rotate(rotation);
    context.font = scaleLabelFont;
    context.textBaseline = 'middle';

    helpers.each(scaleLabel.labelString, function (obj) {
      context.fillStyle = obj.color;
      context.fillText(obj.text, x, 0);
      x += context.measureText(obj.text).width;
    });

    context.restore();
  } else {
    context.save();
    context.translate(scaleLabelX, scaleLabelY);
    context.rotate(rotation);
    context.textAlign = "center";
    context.fillStyle = scaleLabelFontColor; // render in correct colour
    context.font = scaleLabelFont;
    context.textBaseline = 'middle';
    context.fillText(scaleLabel.labelString, 0, 0);
    context.restore();
  }
}

This would allow me to pass in an Array of Objects into the labelString as opposed to just a String. This looks like the following:

'yAxes': [{
  'id': 'y-axis-1',
  'type': 'linear',
  'position': 'left',
  'scaleLabel': {
    'display': true,
    'labelString': [{
      'text': '● ',
      'color': '#fc6062'
    }, {
      'text': 'My Left Label Text',
      'color': '#000'
    }],
    'fontFamily': 'AB',
    'fontColor': '#000',
    'fontSize': 11
  },
}, {
  'id': 'y-axis-2',
  'type': 'linear',
  'position': 'right',
  'scaleLabel': {
    'display': true,
    'labelString': [{
      'text': '● ',
      'color': '#4dc1e7'
    }, {
      'text': 'My Right Label Text',
      'color': '#000'
    }],
    'fontFamily': 'AB',
    'fontColor': '#000',
    'fontSize': 11
  }
}]

Obviously, a change like that should not be done directly in the library. If I were to create a custom Scale though, I would have to override the whole draw function, which is not ideal either as there is a lot of code in there which might change with library versions going forward.

Question is, how would I be able to implement something like this in the least intrusive way? Any suggestions would be greatly appreciated, thanks!

コントリビューターガイド