在具有额外维度的ChartJS上的多行上对y轴标签进行分组 [英] Grouping y-axis labels on multiple lines on a ChartJS with an extra dimension

查看:26
本文介绍了在具有额外维度的ChartJS上的多行上对y轴标签进行分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在SO(Using ChartJS to create a multiple grouped bar chart - see picture below)上成功地成功地使y轴上的第二行标签正确且动态地工作。

我想知道有没有办法让他们走上多条线?查看下图中的标签:

由于它是动态构建的图表,并且行为非常灵敏,所以我的数据有时会超过允许的空间。

因此,低于2.50有时可能太长,以至于溢出到下一个(总计)框中。例如,如果低于2.50的值类似于敏捷的棕色狐狸多次跳过懒狗,并与总数列相遇,则它们将重叠。

我需要找到解决此问题的方法。

推荐答案

TL;DR

看这个答案的最下面(有两种方法,选择第二种):最简单的方法是在数据端使用字符串数组(string [])用于单个类别,并在打印端使用单独的ctx.fillText()调用打印每个数组项。

代码部分1:

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
CanvasRenderingContext2D.prototype.wrapText = function(text, x, y, maxWidth, lineHeight) {

  var lines = text.split("
");
  var linesCount = 0;
  for (var i = 0; i < lines.length; i++) {

    var words = lines[i].split(' ');
    var line = '';

    for (var n = 0; n < words.length; n++) {
      var testLine = line + words[n] + ' ';
      var metrics = this.measureText(testLine);
      var testWidth = metrics.width;
      if (testWidth > maxWidth && n > 0) {
        this.fillText(line, x, y);
        linesCount += 1;
        line = words[n] + ' ';
        y += lineHeight;
      } else {
        line = testLine;
      }
    }

    this.fillText(line, x, y);
    linesCount += 1;
    y += lineHeight;
  }
  return linesCount;
}
new Chart('myChart', {
  type: 'bar',
  plugins: [{
    afterDraw: chart => {
      let ctx = chart.chart.ctx;
      ctx.save();
      let xAxis = chart.scales['x-axis-0'];
      let xCenter = (xAxis.left + xAxis.right) / 2;
      let yBottom = chart.scales['y-axis-0'].bottom;
      ctx.textAlign = 'center';
      ctx.font = '12px Arial';
      var size1 = ctx.wrapText(chart.data.categories[0], (xAxis.left + xCenter) / 2, yBottom + 40, 160, 16);
      var size2 = ctx.wrapText(chart.data.categories[1], (xCenter + xAxis.right) / 2, yBottom + 40, 160, 16);
      var size = size1 > size2 ? size1 : size2;
      chart.options.legend.labels.padding = size * 16 + 5;
      ctx.strokeStyle = 'lightgray';
      [xAxis.left, xCenter, xAxis.right].forEach(x => {
        ctx.beginPath();
        ctx.moveTo(x, yBottom);
        ctx.lineTo(x, yBottom + 40);
        ctx.stroke();
      });
      ctx.restore();
    }
  }],
  data: {
    labels: ['2004', '2008', '2012', '2016', '2004', '2008', '2012', '2016'],
    categories: ['The quick brown fox jumps over the lazy dog', 'Lower than 2.50'],
    datasets: [{
        label: 'Male',
        data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4, 51.2, 51.8],
        backgroundColor: 'rgba(124, 181, 236, 0.9)',
        borderColor: 'rgb(124, 181, 236)',
        borderWidth: 1
      },
      {
        label: 'Female',
        data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6, 53.7, 54.6],
        backgroundColor: 'rgba(67, 67, 72, 0.9)',
        borderColor: 'rgb(67, 67, 72)',
        borderWidth: 1
      }
    ]
  },
  options: {
    legend: {
      position: 'bottom',
      labels: {
        padding: 30,
        usePointStyle: true
      }
    },
    scales: {
      yAxes: [{
        ticks: {
          min: 0,
          max: 80,
          stepSize: 20
        },
        scaleLabel: {
          display: true,
          labelString: 'Percent (%)'
        }
      }],
      xAxes: [{
        gridLines: {
          drawOnChartArea: false
        }
      }]
    }
  }
});
canvas {
  max-width: 400px;
}
<!DOCTYPE html>
<html>

<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
  <canvas id="myChart" height="200"></canvas>
</body>

</html>

解释#1:

我解决的第一件事是解决为什么图表标题在较低的x轴上没有写成两行。在链接的溶液中,使用fillText。它是一种将canvas元素包围起来的方法,但它有一个限制,即您不能在没有工作的情况下将其放在几行上。然而,它可以被删改以适应空间[1],但很快文本就看起来很糟糕。所以,StackOveflow,找到一个解决方法[2]。有几种解决方案,都不容易,但我发现wrapText方法最方便。它可以让文本尽可能长,而且线条都很整齐。

第二个问题是,我必须向下调整图例,以避免同一空间中的多个内容重叠。因此,我向wrapText函数添加了行的计算,并使用它生成了正确的空间量。

在使用该字体的两个线条时,上面的效果很好。例如,一句话:敏捷的棕色狐狸跳过懒惰的狗。然后发生图表中空间的限制:如果内容移到第三行,则从底部剪切图例,因为它不再适合画布。为此,我尝试了以下更改:

  ctx.font = '8px Arial';
  var size1 = ctx.wrapText(chart.data.categories[0], (xAxis.left + xCenter) / 2, yBottom + 40, 160, 8);
  var size2 = ctx.wrapText(chart.data.categories[1], (xCenter + xAxis.right) / 2, yBottom + 40, 160, 8);
  var size = size1 > size2 ? size1 : size2;
  chart.options.legend.labels.padding = size * 8 + 8;

我刚刚将字体大小播放到8像素。它显示所有数据(也显示您的文本),但看起来不是很漂亮。

第1部分的最后说明:

此解决方案是一种变通办法(插件解决方案)。文本最长的最终解决方案看起来如此尴尬,以至于我相信有一个更好的解决方案,但由于我可以做到这一点,我决定分享我的试验,以防没有其他解决方案,所以你至少有这个解决方案。

编辑:更多‘Chart.js’方式,更方便:

代码部分,#2

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
new Chart('myChart', {
  type: 'bar',
  plugins: [{
    beforeDraw: chart => {
      let ctx = chart.chart.ctx;
      ctx.save();    
      let xAxis = chart.scales['x-axis-0'];
      let xCenter = (xAxis.left + xAxis.right) / 2;
      let yBottom = chart.scales['y-axis-0'].bottom;
      ctx.textAlign = 'center';
      ctx.font = '12px Arial';
      ctx.fillText(chart.data.categories[0][0], (xAxis.left + xCenter) / 2, yBottom + 30);
      ctx.fillText(chart.data.categories[0][1], (xAxis.left + xCenter) / 2, yBottom + 40);
      ctx.fillText(chart.data.categories[0][2], (xAxis.left + xCenter) / 2, yBottom + 50);
      ctx.fillText(chart.data.categories[1], (xCenter + xAxis.right) / 2, yBottom + 40);
      ctx.strokeStyle  = 'lightgray';
      [xAxis.left, xCenter, xAxis.right].forEach(x => {
        ctx.beginPath();
        ctx.moveTo(x, yBottom);
        ctx.lineTo(x, yBottom + 40);
        ctx.stroke();
      });
      ctx.restore();
    }
  }],
  data: {
    labels: ['2004', '2008', '2012', '2016', '2004', '2008', '2012', '2016'],
    categories: [['The quick brown fox jumps over', 'the lazy dog a bunch of times','and ran into the Total column'], ['Lower than 2.50']],
    datasets: [{
        label: 'Male',
        data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4, 51.2, 51.8],
        backgroundColor: 'rgba(124, 181, 236, 0.9)',
        borderColor: 'rgb(124, 181, 236)',
        borderWidth: 1
      },
      {
        label: 'Female',
        data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6, 53.7, 54.6],
        backgroundColor: 'rgba(67, 67, 72, 0.9)',
        borderColor: 'rgb(67, 67, 72)',
        borderWidth: 1
      }
    ]
  },
  options: {
    legend: {
      position: 'bottom',
      labels: {
        padding: 30,
    align: 'end',
        usePointStyle: true
      }
    },
    scales: {
      yAxes: [{
        ticks: {
          min: 0,
          max: 80,
          stepSize: 20
        },
        scaleLabel: {
          display: true,
          labelString: 'Percent (%)'
        }
      }],
      xAxes: [{
        gridLines: {
          drawOnChartArea: false
        }
      }]
    }
  }
});
canvas {
  max-width: 400px;
}
<!DOCTYPE html>
<html>
<head>
    <script src="/scripts/snippet-javascript-console.min.js?v=1"></script>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
</body>

说明,第2部分

因此,所有标签、标题等都有两个选项(例如,在[3]上):

  1. 字符串

  2. 字符串[]

选项1都在一行中,猜猜会发生什么:在数组中,您可以逐行定义它。一项是内部数组中的一行!

在您的示例中,这次是自己打印的,所以只需引用字符串数组并定义引用内容的写入位置!

我采用了一种方法,而不是使用较小的字体,将页面上的内容略微放大一点,示例文本正好适合!!

来源:

[1]https://www.w3schools.com/tags/playcanvas.asp?filename=playcanvas_filltextmaxwidth

[2]HTML5 canvas ctx.fillText won't do line breaks?

[3]https://www.chartjs.org/docs/latest/axes/labelling.html

这篇关于在具有额外维度的ChartJS上的多行上对y轴标签进行分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆