Handling axis zoom events via API

Any axis in amCharts 4 can be zoomed. And sometimes we need to know when that happens, as well as the range of the new zoom. This tutorial will walk through all the necessary information to make that happen.

This works a bit differently on different axis types, so we'll explore them separately.

Catching zoom event

Whenever a an axis is zoomed - either via scrollbar, cursor, automatically - depending on which of the selection ends changed the following events are invoked: "startchanged" and/or "endchanged".

Please note that they might be invoked both.

All we have to do to catch such events is attach a handler function to this event.

dateAxis.events.on("startchanged", dateAxisChanged);
dateAxis.events.on("endchanged", dateAxisChanged);
function dateAxisChanged(ev) {
console.log("DateAxis zoomed!");
}
dateAxis.events.on("startchanged", dateAxisChanged);
dateAxis.events.on("endchanged", dateAxisChanged);
function dateAxisChanged(ev: AMEvent<typeof dateAxis, IDateAxisEvents>["startchanged" | "endchanged"]) {
  console.log("DateAxis zoomed!");
}
dateAxis.events.on("startchanged", dateAxisChanged);
dateAxis.events.on("endchanged", dateAxisChanged);
function dateAxisChanged(ev: any) {
  console.log("DateAxis zoomed!");
}
{
// ...
"xAxes": [{
"type": "DateAxis",
// ...
"events": {
"startchanged": function(ev) {
console.log("DateAxis zoomed!");
},
"endchanged": function(ev) {
console.log("DateAxis zoomed!");
}
}
}]
}

NOTE "startchanged" and "endchanged" events are invoked not just when we zoom in or out, but also when we pan the chart, e.g. by dragging a scrollbar.

NOTE Not fancy having two events? If you are using ValueAxis or DateAxis you can use a unified "selectionextremeschanged" event instead.

Of course, just knowing that zoom/pan happened is not very useful in itself. We also need to know the actual range of the new selection. That's what we're going to look into next.

Finding start/end values

Finding a new selection/zoom range depends on the type of the axis. Let's look at each axis type individually.

ValueAxis

Finding current selection on the ValueAxis is quite easy: we just need to access its minZoomed and maxZoomed properties.

valueAxis.events.on("startchanged", valueAxisZoomed);
valueAxis.events.on("endchanged", valueAxisZoomed);
function valueAxisZoomed(ev) {
let start = ev.target.minZoomed;
let end = ev.target.maxZoomed;
console.log("New range: " + start + " -- " + end);
}
valueAxis.events.on("startchanged", valueAxisZoomed);
valueAxis.events.on("endchanged", valueAxisZoomed);
function valueAxisZoomed(ev: AMEvent<typeof dateAxis, IDateAxisEvents>["startchanged" | "endchanged"]) {
  let start = ev.target.minZoomed;
  let end = ev.target.maxZoomed;
  console.log("New range: " + start + " -- " + end);
}
valueAxis.events.on("startchanged", valueAxisZoomed);
valueAxis.events.on("endchanged", valueAxisZoomed);
function valueAxisZoomed(ev) {
var start = ev.target.minZoomed;
var end = ev.target.maxZoomed;
console.log("New range: " + start + " -- " + end);
}
{
// ...
"yAxes": [{
"type": "ValueAxis",
// ...
"events": {
"startchanged": function(ev) {
var start = ev.target.minZoomed;
var end = ev.target.maxZoomed;
console.log("New range: " + start + " -- " + end);
},
"endchanged": function(ev) {
var start = ev.target.minZoomed;
var end = ev.target.maxZoomed;
console.log("New range: " + start + " -- " + end);
}
}
}]
}

DateAxis

DateAxis is basically ValueAxis operating in timestamps.

This is why we can use its minZoomed and maxZoomed as well: those will hold starting and ending timestamps. All we'll need to do is to convert those into Date objects:

dateAxis.events.on("startchanged", dateAxisChanged);
dateAxis.events.on("endchanged", dateAxisChanged);
function dateAxisChanged(ev) {
  let start = new Date(ev.target.minZoomed);
  let end = new Date(ev.target.maxZoomed);
  console.log("New range: " + start + " -- " + end);
}
dateAxis.events.on("startchanged", dateAxisChanged);
dateAxis.events.on("endchanged", dateAxisChanged);
function dateAxisChanged(ev: AMEvent<typeof dateAxis, IDateAxisEvents>["startchanged" | "endchanged"]) {
  let start = new Date(ev.target.minZoomed);
  let end = new Date(ev.target.maxZoomed);
  console.log("New range: " + start + " -- " + end);
}
dateAxis.events.on("startchanged", dateAxisChanged);
dateAxis.events.on("endchanged", dateAxisChanged);
function dateAxisChanged(ev) {
var start = new Date(ev.target.minZoomed);
var end = new Date(ev.target.maxZoomed);
console.log("New range: " + start + " -- " + end);
}
{
// ...
"xAxes": [{
"type": "DateAxis",
// ...
"events": {
"startchanged": function(ev) {
var start = new Date(ev.target.minZoomed);
var end = new Date(ev.target.maxZoomed);
console.log("New range: " + start + " -- " + end);
},
"endchanged": function(ev) {
var start = new Date(ev.target.minZoomed);
var end = new Date(ev.target.maxZoomed);
console.log("New range: " + start + " -- " + end);
}
}
}]
}

CategoryAxis

CategoryAxis is different from other types of axes in that sense that it does not operate in numeric scale, but rather static list of items (categories).

Therefore we're going to be dealing with relative positions and indexes rather than min/max values.

A CategoryAxis has two properties we're going to be using: start and end.

Now, those are relative "position" values, indicating position within the whole range, expressed in values from 0 (zero) to 1 (one).

For example, when the axis is fully zoomed out, start == 0 and end == 1.

If we'd zoom the axis from its middle to the end, we'd have start == 0.5 and end == 1.

Obviously, knowing relative position is not too useful: we need to know precise categories our zoomed selection starts and ends at.

For that CategoryAxis provides a handy method: getPositionLabel(position).

All we have to do to convert a "position" into "category", is to pass it through this method:

categoryAxis.events.on("startchanged", categoryAxisZoomed);
categoryAxis.events.on("endchanged", categoryAxisZoomed);
function categoryAxisZoomed(ev) {
let axis = ev.target;
let start = axis.getPositionLabel(axis.start);
let end = axis.getPositionLabel(axis.end);
console.log("New range: " + start + " -- " + end);
}
categoryAxis.events.on("startchanged", categoryAxisZoomed);
categoryAxis.events.on("endchanged", categoryAxisZoomed);
function categoryAxisZoomed(ev: AMEvent<typeof dateAxis, IDateAxisEvents>["startchanged" | "endchanged"]) {
  let axis = ev.target;
  let start = axis.getPositionLabel(axis.start);
  let end = axis.getPositionLabel(axis.end);
  console.log("New range: " + start + " -- " + end);
}
categoryAxis.events.on("startchanged", categoryAxisZoomed);
categoryAxis.events.on("endchanged", categoryAxisZoomed);
function categoryAxisZoomed(ev) {
var axis = ev.target;
var start = axis.getPositionLabel(axis.start);
var end = axis.getPositionLabel(axis.end);
console.log("New range: " + start + " -- " + end);
}
{
// ...
"xAxes": [{
"type": "CategoryAxis",
// ...
"events": {
"startchanged": function(ev) {
var axis = ev.target;
var start = axis.getPositionLabel(axis.start);
var end = axis.getPositionLabel(axis.end);
console.log("New range: " + start + " -- " + end);
},
"endchanged": function(ev) {
var axis = ev.target;
var start = axis.getPositionLabel(axis.start);
var end = axis.getPositionLabel(axis.end);
console.log("New range: " + start + " -- " + end);
}
}
}]
}

Examples

The following example implements zoom events for both ValueAxis and DateAxis (open your browser console before trying it out):

See the Pen amCharts 4: handling axis zoom events by amCharts team (@amcharts) on CodePen.0

And this one deals with a CategroyAxis:

See the Pen amCharts 4: handling axis zoom events by amCharts team (@amcharts) on CodePen.0