Adding sum labels inside Donut chart

This tutorial will show how we can add labels inside a Donut series, make them display dynamic info, such as sum of values of all slices, as well resize dynamically to comfortably fit within inner section of a chart.

Adding a label

We can add Label element directly as a child of a PieSeries and it will automatically be placed right in the center of the series.

series.children.push(am5.Label.new(root, {
  text: "Hi there!",
  centerX: am5.p50,
  centerY: am5.p50,
  fontSize: 40,
  fontWeight: "500",
  fill: am5.color(0x555555)
}));
series.children.push(am5.Label.new(root, {
  text: "Hi there!",
  centerX: am5.p50,
  centerY: am5.p50,
  fontSize: 40,
  fontWeight: "500",
  fill: am5.color(0x555555)
}));

Positioning

The label will be placed with its X and Y coordinates directly in the center of the pie/donut. This is why we used centerX and centerY settings so that the label is placed by its center, rather than tips top-left corner. This way it will be centered perfectly in the middle.

If we have a multi-line text, we might also use textAlign: "center" setting:

series.children.push(am5.Label.new(root, {
  text: "Hi there!\nšŸ˜‰šŸ˜‰šŸ˜‰",
  centerX: am5.p50,
  centerY: am5.p50,
  textAlign: "center",
  fontSize: 40,
  fontWeight: "500",
  fill: am5.color(0x555555)
}));
series.children.push(am5.Label.new(root, {
  text: "Hi there!\nšŸ˜‰šŸ˜‰šŸ˜‰",
  centerX: am5.p50,
  centerY: am5.p50,
  textAlign: "center",
  fontSize: 40,
  fontWeight: "500",
  fill: am5.color(0x555555)
}));

And finally, should we need to micro-adjust vertical position of the label, we can use dy setting, which moves an element up (negative value) or down by X pixels:

series.children.push(am5.Label.new(root, {
text: "Hi there!",
centerX: am5.p50,
centerY: am5.p50,
fontSize: 40,
fontWeight: "500",
fill: am5.color(0x555555)
}));

Positioning

The label will be placed with its X and Y coordinates directly in the center of the pie/donut. This is why we used centerX and centerY settings so that the label is placed by its center, rather than tips top-left corner. This way it will be centered perfectly in the middle.

If we have a multi-line text, we might also use textAlign: "center" setting:

series.children.push(am5.Label.new(root, {
  text: "Hi there!\nšŸ˜‰šŸ˜‰šŸ˜‰",
  centerX: am5.p50,
  centerY: am5.p50,
  textAlign: "center",
  fontSize: 40,
  fontWeight: "500",
  fill: am5.color(0x555555),
  dy: -10
}));
series.children.push(am5.Label.new(root, {
  text: "Hi there!\nšŸ˜‰šŸ˜‰šŸ˜‰",
  centerX: am5.p50,
  centerY: am5.p50,
  textAlign: "center",
  fontSize: 40,
  fontWeight: "500",
  fill: am5.color(0x555555),
  dy: -10
}));

Displaying series data

Having a label as child of the series comes with another advantage: we can use data placeholders to tap into any public or private setting or property of the parent series.

Among other values, PieSeries holds sum of all its labels in its private setting valueSum. Let's use that in a label:

series.children.push(am5.Label.new(root, {
  text: "[fontSize: 20px; #999]TOTAL[/]\n{valueSum.formatNumber()}",
  textAlign: "center",
  centerX: am5.p50,
  centerY: am5.p50,
  fontSize: 40,
  fontWeight: "500",
  populateText: true,
  fill: am5.color(0x555555)
}));
series.children.push(am5.Label.new(root, {
  text: "[fontSize: 20px; #999]TOTAL[/]\n{valueSum.formatNumber()}",
  textAlign: "center",
  centerX: am5.p50,
  centerY: am5.p50,
  fontSize: 40,
  fontWeight: "500",
  populateText: true,
  fill: am5.color(0x555555)
}));

A few notes about the code above:

  • In order for Label to populate its data placeholders, the populateText: true setting needs to be set.
  • We used in-line text styles to format our label.
  • We also used an in-line formatting function to apply number formatting to the sum placeholder.

Fitting within donut

Finally, let's make sure our label never bleeds out outside of the inner circle, by employing a series event which kicks in when radius of the series changes, to apply maxWidth setting to the label.

We will also set oversizedBehavior: "fit" setting, which will make the label shrink in size, to always fit withing the dynamically-maintained maxWidth:

series.children.push(am5.Label.new(root, {
text: "Hi there!\nšŸ˜‰šŸ˜‰šŸ˜‰",
centerX: am5.p50,
centerY: am5.p50,
textAlign: "center",
fontSize: 40,
fontWeight: "500",
fill: am5.color(0x555555),
dy: -10
}));

Displaying series data

Having a label as child of the series comes with another advantage: we can use data placeholders to tap into any public or private setting or property of the parent series.

Among other values, PieSeries holds sum of all its labels in its private setting valueSum. Let's use that in a label:

let label = series.children.push(am5.Label.new(root, {
  text: "[fontSize: 20px; #999]TOTAL[/]\n{valueSum.formatNumber()}",
  textAlign: "center",
  centerX: am5.p50,
  centerY: am5.p50,
  fontSize: 40,
  fontWeight: "500",
  populateText: true,
  fill: am5.color(0x555555),
  oversizedBehavior: "fit"
}));

// Resize label to fit series
series.onPrivate("radius", function(radius) {
  label.set("maxWidth", radius * 1.5)
});
var label = series.children.push(am5.Label.new(root, {
  text: "[fontSize: 20px; #999]TOTAL[/]\n{valueSum.formatNumber()}",
  textAlign: "center",
  centerX: am5.p50,
  centerY: am5.p50,
  fontSize: 40,
  fontWeight: "500",
  populateText: true,
  fill: am5.color(0x555555),
  oversizedBehavior: "fit"
}));

// Resize label to fit series
series.onPrivate("radius", function(radius) {
  label.set("maxWidth", radius * 1.5)
});
NOTE

For more stuff available in a PieSeries, refer to the related class reference: settings, private settings.

Example

See the Pen Sum label inside Pie Chart by amCharts team (@amcharts) on CodePen.