Making CurveChart labels follow axis angle

CurveChart (or a TimeLine) is a new exciting chart product, allowing twisting and bending the charts into any custom shape. This tutorial will show how you can make labels of a curved axis follow its angle.

Base chart

Let's start with a basic chart example we took from our "TimeLine Chart" article:

See the Pen amCharts 4: Curve chart by amCharts team (@amcharts) on CodePen.24419

The X axis is bent according to our points. However its labels, while positioned correctly, are all statically horizontal.

Let's see how we can make them gently follow the curve.

Solution

The solution comes in form of an adapter.

Basically an adapter is a way to define a custom function that would override value of any property for any object. (read more about adapters)

In this case we'll need to override axis labels' rotation property.

categoryAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
  let value = target.dataItem.category;
  let position = categoryAxis.categoryToPosition(value);
  let angle = categoryAxis.renderer.positionToAngle(position) - 90;
  return angle;
});
categoryAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
  var value = target.dataItem.category;
  var position = categoryAxis.categoryToPosition(value);
  var angle = categoryAxis.renderer.positionToAngle(position) - 90;
  return angle;
});
{
  // ...
  "xAxes": [{
    // ...
    "renderer": {
      "labels": {
        "adapter": {
          "rotation":  function (rotation, target) {
            var categoryAxis = target.baseSprite.xAxes.getIndex(0);
            var value = target.dataItem.category;
            var position = categoryAxis.categoryToPosition(value);
            var angle = categoryAxis.renderer.positionToAngle(position) - 90;
            return angle;
          });
        }
      }
    }
  }]
}

See the Pen amCharts 4: Curve chart by amCharts team (@amcharts) on CodePen.24419

The above example uses CategoryAxis as an X axis. In order to find label's relative position within axis, we used CategoryAxis' method categoryToPosition(category).

Naturally, for other axis types, other methods would need to be used.

ValueAxis would use valueToPosition(value):

valueAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
  let value = target.dataItem.value;
  let position = valueAxis.valueToPosition(value);
  let angle = valueAxis.renderer.positionToAngle(position) - 90;
  return angle;
});
categoryAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
  var value = target.dataItem.value;
  var position = categoryAxis.valueToPosition(value);
  var angle = categoryAxis.renderer.positionToAngle(position) - 90;
  return angle;
});
{
  // ...
  "xAxes": [{
    // ...
    "renderer": {
      "labels": {
        "adapter": {
          "rotation":  function (rotation, target) {
            var valueAxis = target.baseSprite.xAxes.getIndex(0);
            var value = target.dataItem.value;
            var position = valueAxis.valueToPosition(value);
            var angle = valueAxis.renderer.positionToAngle(position) - 90;
            return angle;
          });
        }
      }
    }
  }]
}

While DateAxis would use dateToPosition(date):

dateAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
  let value = target.dataItem.date;
  let position = dateAxis.dateToPosition(value);
  let angle = dateAxis.renderer.positionToAngle(position) - 90;
  return angle;
});
dateAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
  var value = target.dataItem.date;
  var position = dateAxis.dateToPosition(value);
  var angle = dateAxis.renderer.positionToAngle(position) - 90;
  return angle;
});
{
  // ...
  "xAxes": [{
    // ...
    "renderer": {
      "labels": {
        "adapter": {
          "rotation":  function (rotation, target) {
            var dateAxis = target.baseSprite.xAxes.getIndex(0);
            var value = target.dataItem.date;
            var position = dateAxis.dateToPosition(value);
            var angle = dateAxis.renderer.positionToAngle(position) - 90;
            return angle;
          });
        }
      }
    }
  }]
}