最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Chart.js - Positioning Donut Label - Stack Overflow

programmeradmin2浏览0评论

I have a web page that is using Chart.js. In this page, I am rendering three donut charts. In the middle of each chart, I want to show the percentage of the donut that is filled. Currently, I have the following code, which can be seen in this Fiddle.

function setupChart(chartId, progress) {
  var canvas = document.getElementById(chartId);
  var context = canvas.getContext('2d');

  var remaining = 100 - progress;
  var data = {
    labels: [ 'Progress', '', ],
    datasets: [{
      data: [progress, remaining],
      backgroundColor: [
        '#8FF400',
        '#FFFFFF'
      ],
      borderColor: [
        '#8FF400',
        '#408A00'
      ],
      hoverBackgroundColor: [
        '#8FF400',
        '#FFFFFF'
      ]
    }]
  };

  var options = {
    responsive: true,
    maintainAspectRatio: false,
    scaleShowVerticalLines: false,

    cutoutPercentage: 80,
    legend: {
      display: false
    },
    animation: {
      onComplete: function () {
        var xCenter = (canvas.width / 2) - 20;
        var yCenter = canvas.height / 2 - 40;

        context.textAlign = 'center';
        context.textBaseline = 'middle';

        var progressLabel = data.datasets[0].data[0] + '%';
        context.font = '20px Helvetica';
        context.fillStyle = 'black';
        context.fillText(progressLabel, xCenter, yCenter);
      }
    }
  };

  Chart.defaults.global.tooltips.enabled = false;
  var chart = new Chart(context, {
    type: 'doughnut',
    data: data,
    options: options
  });
}

The chart "runs". But the problem is with the rendering of the label. The label is not vertically and horizontally centered within the middle of the donut. The position of the label changes based on the screen resolution too. You can see the label change position by resizing the frame that the charts are rendered in within the Fiddle.

My question is, how do I consistently position the percentage in the middle of the donut? My page is a responsive page so, having consistent positioning is important Yet, I'm not sure what else to do. I feel like the textAlign and textBaseline properties aren't working, or I'm misunderstanding their usage.

Thanks!

I have a web page that is using Chart.js. In this page, I am rendering three donut charts. In the middle of each chart, I want to show the percentage of the donut that is filled. Currently, I have the following code, which can be seen in this Fiddle.

function setupChart(chartId, progress) {
  var canvas = document.getElementById(chartId);
  var context = canvas.getContext('2d');

  var remaining = 100 - progress;
  var data = {
    labels: [ 'Progress', '', ],
    datasets: [{
      data: [progress, remaining],
      backgroundColor: [
        '#8FF400',
        '#FFFFFF'
      ],
      borderColor: [
        '#8FF400',
        '#408A00'
      ],
      hoverBackgroundColor: [
        '#8FF400',
        '#FFFFFF'
      ]
    }]
  };

  var options = {
    responsive: true,
    maintainAspectRatio: false,
    scaleShowVerticalLines: false,

    cutoutPercentage: 80,
    legend: {
      display: false
    },
    animation: {
      onComplete: function () {
        var xCenter = (canvas.width / 2) - 20;
        var yCenter = canvas.height / 2 - 40;

        context.textAlign = 'center';
        context.textBaseline = 'middle';

        var progressLabel = data.datasets[0].data[0] + '%';
        context.font = '20px Helvetica';
        context.fillStyle = 'black';
        context.fillText(progressLabel, xCenter, yCenter);
      }
    }
  };

  Chart.defaults.global.tooltips.enabled = false;
  var chart = new Chart(context, {
    type: 'doughnut',
    data: data,
    options: options
  });
}

The chart "runs". But the problem is with the rendering of the label. The label is not vertically and horizontally centered within the middle of the donut. The position of the label changes based on the screen resolution too. You can see the label change position by resizing the frame that the charts are rendered in within the Fiddle.

My question is, how do I consistently position the percentage in the middle of the donut? My page is a responsive page so, having consistent positioning is important Yet, I'm not sure what else to do. I feel like the textAlign and textBaseline properties aren't working, or I'm misunderstanding their usage.

Thanks!

Share Improve this question asked Nov 30, 2016 at 13:41 user687554user687554 11.1k26 gold badges81 silver badges142 bronze badges 1
  • Do you prefer to use the version of Chart.JS that you have on your fiddle, or would you consider using V2.1? – Ivan Chaer Commented Dec 8, 2016 at 8:53
Add a ment  | 

5 Answers 5

Reset to default 4 +75

According to this answer, you could do it with a plugin in a further version. I don't know if you noticed, but if you increase the width and reduce it over and over again on your fiddle, the height of your canvas gets bigger and bigger (the chart goes further and further down).

On the version you are using, you could extend the doughnut controller like this:

http://jsfiddle/ivanchaer/cutkqkuz/1/

Chart.defaults.DoughnutTextInside = Chart.helpers.clone(Chart.defaults.doughnut);
Chart.controllers.DoughnutTextInside = Chart.controllers.doughnut.extend({
    draw: function(ease) {

        var ctx = this.chart.chart.ctx;
        var data = this.getDataset();

        var easingDecimal = ease || 1;
        Chart.helpers.each(this.getDataset().metaData, function(arc, index) {
            arc.transition(easingDecimal).draw();

            var vm = arc._view;
            var radius = (vm.outerRadius + vm.innerRadius) / 2;
            var thickness = (vm.outerRadius - vm.innerRadius) / 2;
            var angle = Math.PI - vm.endAngle - Math.PI / 2;

            ctx.save();
            ctx.fillStyle = vm.backgroundColor;

            ctx.font = "20px Verdana";
            ctx.textAlign = 'center';
            ctx.textBaseline = "middle";

            var text = data.data[0] + '%';
            ctx.fillStyle = '#000';
            ctx.fillText(text, $(ctx.canvas).width()/2, $(ctx.canvas).height()/2);
        });
        ctx.fillStyle = '#fff';
        ctx.fill();
        ctx.restore();

    }

});


function setupChart(chartId, progress) {
    var canvas = document.getElementById(chartId);
    var context = canvas.getContext('2d');
    var remaining = 100 - progress;

    var deliveredData = {
        labels: [
            "Value"
        ],
        datasets: [{
            data: [progress, remaining],
            backgroundColor: [
                '#8FF400',
                '#FFFFFF'
            ],
            borderColor: [
                '#8FF400',
                '#408A00'
            ],
            hoverBackgroundColor: [
                '#8FF400',
                '#FFFFFF'
            ]
        }]
    };

    var deliveredOpt = {
        cutoutPercentage: 88,

        animation: false,
        legend: {
            display: false
        },
        tooltips: {
            enabled: false
        }
    };

    var chart = new Chart(canvas, {
        type: 'DoughnutTextInside',
        data: deliveredData,
        options: deliveredOpt
    });

}

setupChart('standChart', 33);
setupChart('moveChart', 66);
setupChart('exerciseChart', 99);

See this http://jsfiddle/uha5tseb/2/

It can be handled with getting width/height of each chart by this reference

  onComplete: function (event) {
    console.log(this.chart.height);
    var xCenter = this.chart.width/2;
    var yCenter = this.chart.height/2;

    context.textAlign = 'center';
    context.textBaseline = 'middle';

    var progressLabel = data.datasets[0].data[0] + '%';
    context.font = '20px Helvetica';
    context.fillStyle = 'black';
    context.fillText(progressLabel, xCenter, yCenter);
  }

Hope this solves your problem!

You set your variables incorrectly when you get xCenter and yCenter. Currently they don't get the middle of the canvas.

You have:

var xCenter = (canvas.width / 2) - 20;
var yCenter = canvas.height / 2 - 40;

It should be:

var xCenter = (canvas.width / 2);
var yCenter = (canvas.height / 2);

Im not familiar with chart.js, but as far as I understood from the documentation, they provide generateLegend method for your purposes. It returns a data from legendCallback that you can display on the page outside of canvas.

Container of the legend text can be aligned relatively to the canvas's wrapper.

HTML:

<div class="chart" data-legend="">
  <canvas id="standChart"></canvas>
</div>

JS:

var container = canvas.parentElement;
...
container.setAttribute('data-legend', chart.generateLegend());

Fiddle

One way to resolve this is by adding an absolutely positioned element on top of the canvas.

You can add a div element after each canvas element and then set its style to:

.precentage {
  font-family: tahoma;
  font-size: 28px;
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
}

Here is a plete JSFiddle.

发布评论

评论列表(0)

  1. 暂无评论