Search code examples
javascriptamcharts

Is there a way to center label in nested pie chart without using magic numbers as radius for Amchart v4?


My problem: i would like to find an automatic way to center labels in donut chart cells. In my case each cells contains an array of complex objects, what i want is to show the number of those objects.

See: enter image description here

Playing with radius, allowed me to find those values:

  • First layer: -28
  • Second layer: -20
  • Third layer: -10
  • Fourth layer: -8

I applied it as a quick fix but i don't like this solution, as it's fixed for 4 layers (what if I need to add an other layer ? etc....) and using "magic numbers" is unmaintenable...

Do you have a better solution ?

You can test it here: https://jsfiddle.net/aliz/gwz7om9e/

Line 40:

 pieSeries.labels.template.radius = am4core.percent(positionRadiusInPie);

Note: Using those attributes didn't work: "horizontalCenter", "VerticalCenter", "textAlign", "align".

EDIT: response to Bhavik Kalariya

This is what I get if I force one radius for all layers. enter image description here


Solution

  • You can at least get it down to just using one base constant of your choosing by using an adapter approach on the label's radius to calculate the value you want for each series that is added to the chart using whatever formula you choose. Here's a basic formula that gave good results for me with your setup, where BASE_INNER_LABEL_RADIUS is set to -45.

        pieSeries.labels.template.adapter.add('radius', function(radius, target) {
          var chart = target.baseSprite; //chart stored in baseSprite of the AxisCircularLabel
          var index = chart.series.indexOf(target.dataItem.component); //corresponding series stored in the dataItem's component
    
          return am4core.percent(BASE_INNER_LABEL_RADIUS / (index + 2)); //Uses singular constant defined elsewhere, which is used in all series
        });
    

    This will adjust itself according to the number of series you add to the chart. You can make this as robust as you want by making further tweaks if you have fewer series and want to make the labels even more centered.

    Updated fiddle

    Edit

    If you want to be even more generic, you can get at the slice sprite directly through the target.dataItem.sprites array (typically the first element, though you can loop through and look for an object whose className is "Slice" if you want to be super safe about it) and calculate your desired radius value using any of the numeric properties it has, such as innerRadius.

        pieSeries.labels.template.adapter.add('radius', function(radius, target) {
          var chart = target.baseSprite; //chart stored in baseSprite of the AxisCircularLabel
          var index = chart.series.indexOf(target.dataItem.component); //corresponding series stored in the dataItem's component
          return -(target.dataItem.sprites[0].innerRadius) / (index + 3); 
        });
    

    You'll want to adjust this accordingly, of course.

    Fiddle