chartjs / Chart.js

Simple HTML5 Charts using the <canvas> tag
https://www.chartjs.org/
MIT License
64.77k stars 11.92k forks source link

Label inside donut chart #78

Closed jomarmen closed 6 years ago

jomarmen commented 11 years ago

Hi,

I would like to be able to add a label inside a donut chart like this charts

lnDId

Thks

helmund commented 11 years ago

me too +1

battaglr commented 11 years ago

+1

ax003d commented 11 years ago

+1

dibba commented 11 years ago

+1

michelreyes commented 11 years ago

+1

nnnick commented 11 years ago

Would putting this data in an HTML absolutely positioned element over the top of the doughnut not be a better solution than adding more to chart.js to achieve this?

fizerkhan commented 11 years ago

+1

jcutrell commented 11 years ago

It's fairly easy to do this with HTML, but it would be nice if the chart exposed some sort of event API to hook into to populate the value.

bissy commented 11 years ago

+1

SteveD11 commented 11 years ago

+1

J5Dev commented 11 years ago

+1

Although, as well as a label I think it would also be good to have it auto add the sum-total of the data put in, or the data type etc

I may work on a way to pass this in if I get the chance this week, though doing the total automatically shouldn't be too much of a stretch.

deanhume commented 11 years ago

+1

ghost commented 10 years ago

is there any option to write label for each section?

erikthedeveloper commented 10 years ago

+1

Yes, this can be accomplished easily with HTML, but I would love to see something like this implemented into the core.

joetime commented 10 years ago

+1

carlosrymer commented 10 years ago

+1

hutber commented 10 years ago

+2

marcj commented 10 years ago

+3

fulldecent commented 10 years ago

-1 I think the HTML approach is better. The label will only make sense inside the canvas if it is very small.

I would consider these to be a special-purpose of pie chart: single value pie charts with label in the middle.

Recommending implementation should be outside chart.js

joetime commented 10 years ago

@fulldecent The advantage to having a label/legend built into the chart is that the user can take the image itself and use it in another doc or presentation without having to re-create the legend. In fact, for my users, this is an absolute requirement - a chart without a legend is useless to them. Whether that should be within the scope of chart.js is debatable, but I would guess there are many devs that would agree with me on this one. I could be wrong though.

304NotModified commented 9 years ago

+1

andqwerty commented 9 years ago

+1

fulldecent commented 9 years ago

tagging: NEW CHART TYPES

fixanoid commented 9 years ago

This may be useful for some of you since I've had the same requirement a few weeks ago. https://github.com/fixanoid/Pretty-Doughtnut

kiquenet commented 9 years ago

Any solution about it (http://stackoverflow.com/questions/20911919/put-sum-of-values-in-center-of-doughnut-chart) ?

heyallan commented 9 years ago

+1

tannerlinsley commented 9 years ago

I don't think having a label in the middle of the chart is quite yet needed in the core logic of chartjs. We will probably soon be adding new event-hook-like api's into the core though, that would allow you to achieve this quite easily in a custom implementation along with a lot of other things.

obsidianart commented 8 years ago

the main problem I can see with your design is the top left slice (0%), evaluating those exception is probably one of the reason why your design is not implemented.

obsidianart commented 8 years ago

yes, I got it. What do you do when the slice is small?

peterzebird commented 8 years ago

+1

p0fi commented 8 years ago

+1

dannysindra commented 8 years ago

+1

As joetime mentioned above, this feature will be pretty useful for clients. Currently mine are making the charts manually from an Excel file, then copy-paste the pie charts into their PPT files.

screen shot 2016-04-26 at 5 05 36 pm

Please consider adding this feature, as I saw it there are so many requests since 2013 for this 👍

Also, I would love to know how to make the pie slices show percentage instead of raw number. Anyone has found a way to achieve these things temporarily? Any help will be much appreciated!

adamrainsby commented 8 years ago

+1

dannysindra commented 8 years ago

Still no update on whether this request will be implemented? For anyone interested on pie chart, I found a workaround by forcing the tooltip to always appear (from this answer on SO):

screen shot 2016-05-02 at 6 57 40 pm

Here is the config, you will need to modify the animation property:

var options = {
    ...
    events: false,
    animation: {
      duration: 0,
      onComplete: function () {
        var self = this;

        var elementsArray = [];
        Chart.helpers.each(self.data.datasets, function (dataset, datasetIndex) {
            Chart.helpers.each(dataset.metaData, function (element, index) {
                var tooltip = new Chart.Tooltip({
                    _chart: self.chart,
                    _chartInstance: self,
                    _data: self.data,
                    _options: self.options,
                    _active: [element]
                }, self);

                tooltip.update();
                tooltip.transition(Chart.helpers.easingEffects.linear).draw();
            }, self);
        }, self);
      }
    },
   ...
}

As you can see, the tooltips are overlapping each other, and some tooltips still appears if the segments contain zero data. Anyone knows how to fix this? I also want to change the balloons to appear above, instead of from left or right. The pie chart below is a good example:

screen shot 2016-05-02 at 7 05 33 pm

Any help will be appreciated!

NoRoboto commented 8 years ago

+1

dannysindra commented 8 years ago

Hi guys, starting from chart.js v2.1, I was able to get the pie chart to show labels in percentages. Here is my code - hopefully it will work for everyone. Put this inside your chart options:

   ...,
   animation: {
      duration: 0,
      onComplete: function () {
        var self = this,
            chartInstance = this.chart,
            ctx = chartInstance.ctx;

        ctx.font = '18px Arial';
        ctx.textAlign = "center";
        ctx.fillStyle = "#ffffff";

        Chart.helpers.each(self.data.datasets.forEach((dataset, datasetIndex) => {
            var meta = self.getDatasetMeta(datasetIndex),
                total = 0, //total values to compute fraction
                labelxy = [],
                offset = Math.PI / 2, //start sector from top
                radius,
                centerx,
                centery, 
                lastend = 0; //prev arc's end line: starting with 0

            for (var val of dataset.data) { total += val; } 

            Chart.helpers.each(meta.data.forEach((element, index) => {
                radius = 0.9 * element._model.outerRadius - element._model.innerRadius;
                centerx = element._model.x;
                centery = element._model.y;
                var thispart = dataset.data[index],
                    arcsector = Math.PI * (2 * thispart / total);
                if (element.hasValue() && dataset.data[index] > 0) {
                  labelxy.push(lastend + arcsector / 2 + Math.PI + offset);
                }
                else {
                  labelxy.push(-1);
                }
                lastend += arcsector;
            }), self)

            var lradius = radius * 3 / 4;
            for (var idx in labelxy) {
              if (labelxy[idx] === -1) continue;
              var langle = labelxy[idx],
                  dx = centerx + lradius * Math.cos(langle),
                  dy = centery + lradius * Math.sin(langle),
                  val = Math.round(dataset.data[idx] / total * 100);
              ctx.fillText(val + '%', dx, dy);
            }

        }), self);
      }
    },
    ...
screen shot 2016-05-09 at 11 05 43 am

Cheers

djoos commented 8 years ago

Anyone any tips on achieving a label inside of a donut chart? (as per the original issue) It would be great to find out how to do this in 2.x...

Thanks in advance!

MaZZly commented 8 years ago

Yes please add functionality for this..

Tried with the html element solution but that won't work if you want it to be responsive.. Also can't always calculate the position from the canvas as the amount of labels might mess upp the position of the donut itself

potatopeelings commented 8 years ago

Here's one way to do it using the newly added plugin service - http://jsfiddle.net/s9tu1c9y/

Yaay plugins!

jaddx1


If you don't need the plugin to figure out the font size, you can probably take out a bit of code - this bit of code treats the text as a line while trying to fit it in the inner diameter. If you want to be really accurate, you should actually treat it as a rectangle but I figure its not worth the complexity - if the corners of your text overlap with the chart sectors, just add some spaces before and after your maxText

The plugin code

Chart.pluginService.register({
    afterUpdate: function (chart) {
        if (chart.config.options.elements.center) {
            var helpers = Chart.helpers;
            var centerConfig = chart.config.options.elements.center;
            var globalConfig = Chart.defaults.global;
            var ctx = chart.chart.ctx;

            var fontStyle = helpers.getValueOrDefault(centerConfig.fontStyle, globalConfig.defaultFontStyle);
            var fontFamily = helpers.getValueOrDefault(centerConfig.fontFamily, globalConfig.defaultFontFamily);

            if (centerConfig.fontSize)
                var fontSize = centerConfig.fontSize;
            // figure out the best font size, if one is not specified
            else {
                ctx.save();
                var fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
                var maxFontSize = helpers.getValueOrDefault(centerConfig.maxFontSize, 256);
                var maxText = helpers.getValueOrDefault(centerConfig.maxText, centerConfig.text);

                do {
                    ctx.font = helpers.fontString(fontSize, fontStyle, fontFamily);
                    var textWidth = ctx.measureText(maxText).width;

                    // check if it fits, is within configured limits and that we are not simply toggling back and forth
                    if (textWidth < chart.innerRadius * 2 && fontSize < maxFontSize)
                        fontSize += 1;
                    else {
                        // reverse last step
                        fontSize -= 1;
                        break;
                    }
                } while (true)
                ctx.restore();
            }

            // save properties
            chart.center = {
                font: helpers.fontString(fontSize, fontStyle, fontFamily),
                fillStyle: helpers.getValueOrDefault(centerConfig.fontColor, globalConfig.defaultFontColor)
            };
        }
    },
    afterDraw: function (chart) {
        if (chart.center) {
            var centerConfig = chart.config.options.elements.center;
            var ctx = chart.chart.ctx;

            ctx.save();
            ctx.font = chart.center.font;
            ctx.fillStyle = chart.center.fillStyle;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            var centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
            var centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
            ctx.fillText(centerConfig.text, centerX, centerY);
            ctx.restore();
        }
    },
})

and then

    ...
    options: {
        elements: {
            center: {
                // the longest text that could appear in the center
                maxText: '100%',
                text: '90%',
                fontColor: '#36A2EB',
                fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
                fontStyle: 'normal',
                // fontSize: 12,
                // if a fontSize is NOT specified, we will scale (within the below limits) maxText to take up the maximum space in the center
                // if these are not specified either, we default to 1 and 256
                minFontSize: 1,
                maxFontSize: 256,
            }
        }
    }
};
LuisMCunha commented 8 years ago

@dannysindra thank you very much for your example, it works very well, but the percentages flicker every time I mouse over the pie chart, is there any way to avoid this?

dannysindra commented 8 years ago

@MoPHL I have not solved that part completely, but there is a way to prevent flicker on "mouseover" event (still flicker on "mouseclick").

To do it simply empty the events list array:

   ...,
   events: [""],
   animation: {
      ...
   }

Empty array will not do, so I supplied empty string inside the array to make it work. Note that the tooltip will also not appear on mouseover.

LuisMCunha commented 8 years ago

@dannysindra I have also tried disabling the events, but I still need to retain the tooltips, etc.

Gabweb commented 8 years ago

By combining both solutions from @potatopeelings and @dannysindra I manged to get it work. It won't flicker and after some changes to those forEach() it now works on iOS too.

I Implemented two options to either show the labels or percentages. Take a look at the Code: http://jsfiddle.net/Gabweb/s15jj21a/

workingpielabels

jorgecgll commented 8 years ago

In my case I had to implement it differently in order to achieve animated percentages - (even when you hide and show segments). Still needs some work.

http://jsfiddle.net/g6fajwg8/

It would be nice if we had this implemented in the core :) .. Although I'm seeing comments that it's better to implement it in other ways, so I might be wrong.

LuisMCunha commented 8 years ago

@jorgecgll very nice, it's working very well and found no issues so far.

dsozzi commented 8 years ago

I'm using the plugin above and it works like a charm. I'm currently updating the chart value every 5s and I was wondering if there is a way to trigger the redraw of the element text.

thanks

djoos commented 8 years ago

👍

mauricionr commented 8 years ago

👍

Reflic commented 8 years ago

Whats the status on this issue?

fulldecent commented 8 years ago

The status is: waiting for pull request. Please use the notification feature on the right side of the screen here to be automatically notified for further status updates.