【问题标题】:ChartJS 2.0 - Huddle labels on pie chartChartjs 2.0 - 饼图上的挤出标签
【发布时间】:2017-02-17 10:05:11
【问题描述】:

我需要绘制具有许多值(其中一部分接近)和长值标签的图表。这些值应显示在图表上(而不是图例)。我正在使用以下插件来显示标签:

var drawItemsValuesPlugin = {
    afterDraw: function(chartInstance) {

        var ctx = chartInstance.chart.ctx;

        // render the value of the chart above the bar
        ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily);
        ctx.textAlign    = 'center';
        ctx.textBaseline = 'bottom';        
        ctx.strokeStyle = 'black';
        ctx.font = "bold 11pt Arial";
        ctx.lineWidth = 0.3;

        chartInstance.data.datasets.forEach(function (dataset) {
                for (var i = 0; i < dataset.data.length; i++) {    
                    var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
                        total = dataset._meta[Object.keys(dataset._meta)[0]].total,
                        mid_radius = model.innerRadius + (model.outerRadius - model.innerRadius)/2,
                        start_angle = model.startAngle,
                        end_angle = model.endAngle,
                        mid_angle = start_angle + (end_angle - start_angle)/2;

                    var fact = (chartInstance.config.type == 'pie'?
                        (i%2==0?1:1.6):
                        (i%2==0?0.9:1.1)
                    );
                    var x = mid_radius * fact * Math.cos(mid_angle);
                    var y = mid_radius * fact * Math.sin(mid_angle);

                    ctx.fillStyle = '#fff';

                    var label = chartInstance.config.data.labels[i];
                    var percent = String(Math.round(dataset.data[i]/total*100)) + "%";
                    ctx.fillText(label, model.x + x, model.y + y);
                    ctx.strokeText(label, model.x + x, model.y + y);
                    // Display percent in another line, line break doesn't work for fillText
                    ctx.fillText(dataset.data[i] + ' ('+percent+')', model.x + x, model.y + y + 15);
                    ctx.strokeText(dataset.data[i] + ' ('+percent+')', model.x + x, model.y + y + 15);
                }
            });
    }
};

但我面临以下问题:

值标签是杂乱无章的。一个简单的解决方案是将标签围绕图表放置,如下图所示:

如何使用 Chart JS 2.0 获取此标签位置?

【问题讨论】:

  • 你可以试试:mid_radius = 1.2 * model.outerRadius;,看看你会得到什么。

标签: javascript charts html5-canvas chart.js


【解决方案1】:

注意:以下代码无效。只是一个布局。

x,y = 0,0 = middle of pie chart / middle of the circle

//这 4 个点 = 甜甜圈派的一部分。

xa[1],ya[1] = x,y point on the outside of circle.
xb[1],yb[1] = x,y point on the outside of circle.
xc[1],yc[1] = x,y point on the inside of circle.
xd[1],yd[1] = x,y point on the inside of circle.

//只为外圆寻找上述坐标的中点

xab[1],yab[1] = x,y point on the outside circle, between xa,ya and xb,yb

//你需要找到以像素为单位的文本高度。

function somefunc(){
  //i do know how to do this. 
}

//这是文本的一半高度,因此可以将线条绘制到文本的中点。

var label_height_pixel[1] = //no idea how to figure this out
var find_middle_of_text[1] = label_height_pixel[1] / 2;

//制作一个更大的圆圈,然后添加一点填充,这样文本就不会在圆圈上。

//ok 错误代码找出 x,y 坐标是什么。使用

xab[1],yab[1] and x,y
var xtext[1],ytext[1] = outer_circle_radius + find_middle_of_text[1] + padding

对上面的所有内容运行一个 for 循环。 更改上面的最后一个代码。这样它每次增加一点。所以标签不会相互重叠。

my_next_height[2] = outer_circle_radius + label_height_pixel[1] + find_middle_of_text[2] + padding
my_next_height[3] = outer_circle_radius + label_height_pixel[1] + label_height_pixel[2] + find_middle_of_text[3] + padding
my_next_height[4] = outer_circle_radius + label_height_pixel[1] + label_height_pixel[2] + label_height_pixel[3] + find_middle_of_text[3] + padding

您需要在其中放入一个 if/then/else 函数来检查 x 是正数还是负数。并从 my_next_height[] 向左或向右画线。

然后画出你的文字。

从外圈向外画线,然后向外画线,然后放置标签,还有更多工作要做。但上面的代码布局被切碎,应该给你足够的坐标来实现这一点。

我尽量避免 x、y 和 z 坐标。如果不打开旧的小学数学书,我永远无法记住数学来找到点和角度。并查看您的代码 mid_radius 以及所有代码,我没有看到我在找到正确的 x,y 绘图点时记得的正常数学。

【讨论】:

    【解决方案2】:

    如果可以帮助你,试试这个,作为charjs的简单解决方案

    $.plot('#placeholder', data, {
        series: {
            pie: {
                show: true
            }
        },
        legend: {
            show: false
        }
    });
    

    【讨论】:

      【解决方案3】:

      这是一个草稿答案。它的主要目的是给你希望。不可能是最终的(几天后会是最终的),因为:

      • 代码很丑陋、被黑、没有文档。它基于您提供的插件。
      • 代码需要 Chart.js 2 的下一个小版本(可能是 2.3.1)。我已经构建了今天的 Chart.js 主分支并将其上传到我的 Dropbox,因为需要最近合并的 layout.padding 选项,以便在饼图周围为轨道标签提供空间(没有此选项标签会被切断) .
      • 肯定需要改进。

      这里是demo

      一条小线会将饼图的每个切片与轨道中的相应标签连接起来。连续的标签(除了第一个和最后一个,如果数据集有奇数个元素)将以不同的半径运行,这样可以避免冲突。但我想,需要更多的照顾。主要问题是如何确保图表是响应式的。 layout.padding 指的是像素,而不是百分比。此外,标签字体目前在大小方面是固定的。我认为只需将标签的位置设置为距画布边框的固定距离(因为填充也是固定的)可以解决该问题(调整图表大小会给出一些想法,但稍后会详细介绍)。

      demo 下面也有,但是它无法从 Dropbox 加载 JavaScript 文件(而 JSFiddle 应该不会失败 - 当 Chart.js 的 CDN 版本更新时,我会更新所有链接):

      var drawItemsValuesPlugin = {
        afterDraw: function(chartInstance) {
      
          var ctx = chartInstance.chart.ctx;
      
          // render the value of the chart above the bar
          ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily);
          ctx.textAlign = 'center';
          ctx.textBaseline = 'bottom';
          ctx.strokeStyle = 'black';
          ctx.font = "bold 11pt Arial";
          ctx.lineWidth = 0.3;
      
          chartInstance.data.datasets.forEach(function(dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
              var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
                total = dataset._meta[Object.keys(dataset._meta)[0]].total,
                mid_radius = model.innerRadius + (2.4 * model.outerRadius - model.innerRadius) / 2,
                start_angle = model.startAngle,
                end_angle = model.endAngle,
                mid_angle = start_angle + (end_angle - start_angle) / 2;
      
              var mid_radius2 = 0.92 * (model.innerRadius + (2.4 * model.outerRadius - model.innerRadius) / 2);
      
              var fact = i % 2 == 0 ? 1.00 : 1.15;
      
              var x = mid_radius * fact * Math.cos(mid_angle);
              var y = mid_radius * fact * Math.sin(mid_angle);
      
              var myX = mid_radius2 * fact * Math.cos(mid_angle);
              var myY = mid_radius2 * fact * Math.sin(mid_angle);
      
              var myX2 = 0.91 * mid_radius2 * 1 * Math.cos(mid_angle);
              var myY2 = 0.91 * mid_radius2 * 1 * Math.sin(mid_angle);
      
              ctx.lineWidth = 2;
              ctx.beginPath();
              ctx.moveTo(model.x + myX2, model.y + myY2);
              ctx.strokeStyle = '#232323';
              ctx.lineTo(model.x + myX, model.y + myY);
              ctx.stroke();
              ctx.strokeStyle = 'black';
              ctx.fillStyle = '#454545';
              ctx.lineWidth = 0.3;
      
              var label = chartInstance.config.data.labels[i];
              var percent = String(Math.round(dataset.data[i] / total * 100)) + "%";
              ctx.fillText(label, model.x + x, model.y + y);
              ctx.strokeText(label, model.x + x, model.y + y);
              // Display percent in another line, line break doesn't work for fillText
              ctx.fillText(dataset.data[i] + ' (' + percent + ')', model.x + x, model.y + y + 15);
              ctx.strokeText(dataset.data[i] + ' (' + percent + ')', model.x + x, model.y + y + 15);
            }
          });
        }
      };
      
      Chart.pluginService.register(drawItemsValuesPlugin);
      
      var ctx = document.getElementById("myChart");
      
      var myChart = new Chart(ctx, {
        type: 'pie',
        data: {
          labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
          datasets: [{
            data: [15, 1, 1, 1, 45, 1],
            backgroundColor: [
              'rgba(255, 99, 132, 0.2)',
              'rgba(54, 162, 235, 0.2)',
              'rgba(255, 206, 86, 0.2)',
              'rgba(75, 192, 192, 0.2)',
              'rgba(153, 102, 255, 0.2)',
              'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
              'rgba(255,99,132,1)',
              'rgba(54, 162, 235, 1)',
              'rgba(255, 206, 86, 1)',
              'rgba(75, 192, 192, 1)',
              'rgba(153, 102, 255, 1)',
              'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
          }]
        },
        options: {
          legend: {
            display: false
          },
          layout: {
            padding: 140
          }
        }
      });
      <script src="https://dl.dropboxusercontent.com/s/yju3s0dqe32um2k/Chart.min.js"></script>
      <canvas id="myChart" width="400" height="400"></canvas>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-06
        • 2019-01-31
        • 1970-01-01
        • 2013-07-07
        • 2017-01-07
        相关资源
        最近更新 更多