This tutorial will explain how you can track Cursor's position within plot area, as well as map it to real values.
Prerequisites
If you haven't done so already, we strongly suggest you take a look at the "Chart Cursor" article, which walks through every step and aspect of using this chart helper.
We will also be relying on event handlers in this tutorial, so it might be a good idea to familiarize yourself with the concept by checking out "Event Listeners" article.
Tracking Cursor's position
Getting position
To track Cursor's position we will be using its cursorpositionchanged
event.
It will be invoked every time a cursor's position changes over the plot area.
It goes something like this:
chart.cursor.events.on("cursorpositionchanged", function(ev) { // ... });
chart.cursor.events.on("cursorpositionchanged", function(ev) { // ... });
{ // ... "cursor": { "events": { "cursorpositionchanged": function(ev) { // .. } } } }
Now, whenever you glide your mouse over or touch chart's plot area, our event handle will fire. At this point it does nothing since we haven't added any code to it, yet.
Let's see how we can make it useful.
The event handler function will receive a parameter object which, among other properties, will have target
property that holds reference to cursor itself.
Since we now have Cursor's object, we can examine its reference and find out that it has properties xPosition
and yPosition
that respectively hold cursor's relative horizontal and vertical positions within plot area.
chart.cursor.events.on("cursorpositionchanged", function(ev) { console.log("x: ", ev.target.xPosition); console.log("y: ", ev.target.yPosition); });
chart.cursor.events.on("cursorpositionchanged", function(ev) { console.log("x: ", ev.target.xPosition); console.log("y: ", ev.target.yPosition); });
{ // ... "cursor": { "events": { "cursorpositionchanged": function(ev) { console.log("x: ", ev.target.xPosition); console.log("y: ", ev.target.yPosition); } } } }
As we mentioned earlier, the position properties will show cursor's relative position within plot area, where 0
indicates left/top edge, 0.5
- the middle, and 1
- right/bottom edge.
Not very useful, huh?
Accounting for zoom
There's a catch. It's important. Pay close attention to the next few paragraphs.
A Cursor's xPosition
and yPosition
corespond to its horizontal and vertical position within plot area (plotContainer
). Plot area's dimensions are constant and never change.
This might not be the case with chart's axes. While chart is fully zoomed out, the position within plot area (cursor's position) and position within chart's axes are identical. However, when you zoom in, position within axis and plot area become misaligned.
E.g. Cursor's position 0.2
will always point to the 20% from the left of the plot area. However, same value position in horizontal axis scope might point to a wholly different place, even to a point that's currently outside plot area.
This is why you should always convert Cursor's position to Axis' position, using the latter's toAxisPosition()
.
This Axis' method converts Cursor's (plot area) position into particular axis position, taking zoom into account.
We'll show you how to use it in the next chapter.
Converting to real value, date, or category
The next obvious step is to convert relative "position" to a real value within real axis scale.
Lucky for us, each type of axis has helper functions that will convert position to a real value within it's scale.
Below are listed the functions that will help us:
Axis type | Conversion function | Comment |
---|---|---|
All axis types | getPositionLabel(position) |
Returns an actual label that is or would be at that specific position on axis. |
CategoryAxis |
positionToIndex(position) |
Returns an index of the category that corresponds relative position. |
DateAxis |
positionToDate(position) |
Returns a Date object that corresponds to relative position. |
ValueAxis |
positionToValue(position) |
Returns a numeric value that corresponds to the relative position. |
So, if our chart has a horizontal Date axis, and a vertical Value axis, we could use positionToDate
and positionToValue
respectively to convert Cursor's relative xPosition
and yPosition
to real values.
chart.cursor.events.on("cursorpositionchanged", function(ev) { let xAxis = ev.target.chart.xAxes.getIndex(0); let yAxis = ev.target.chart.yAxes.getIndex(0); console.log("x: ", xAxis.positionToDate(xAxis.toAxisPosition(ev.target.xPosition))); console.log("y: ", yAxis.positionToValue(yAxis.toAxisPosition(ev.target.yPosition))); });
chart.cursor.events.on("cursorpositionchanged", function(ev) { var xAxis = ev.target.chart.xAxes.getIndex(0); var yAxis = ev.target.chart.yAxes.getIndex(0); console.log("x: ", xAxis.positionToDate(xAxis.toAxisPosition(ev.target.xPosition))); console.log("y: ", yAxis.positionToValue(yAxis.toAxisPosition(ev.target.yPosition))); });
{ // ... "cursor": { "events": { "cursorpositionchanged": function(ev) { var xAxis = ev.target.chart.xAxes.getIndex(0); var yAxis = ev.target.chart.yAxes.getIndex(0); console.log("x: ", xAxis.positionToDate(xAxis.toAxisPosition(ev.target.xPosition))); console.log("y: ", yAxis.positionToValue(yAxis.toAxisPosition(ev.target.yPosition))); } } } }
Now, when you will glide the Cursor of the plot area, you will be getting meaningful x and y coordinates, expressed in values that correspond to the axis type and scale.
Logging last position
The last thing remaining is to log this data somewhere, so that we can access it later, when we need it, perhaps on click.
For that we can define a variable that holds it:
let cursorPosition = { x: null, y: null }; chart.cursor.events.on("cursorpositionchanged", function(ev) { let xAxis = ev.target.chart.xAxes.getIndex(0); let yAxis = ev.target.chart.yAxes.getIndex(0); cursorPosition.x = xAxis.positionToDate(xAxis.toAxisPosition(ev.target.xPosition)); cursorPosition.y = yAxis.positionToValue(yAxis.toAxisPosition(ev.target.yPosition)); });
var cursorPosition = { x: null, y: null }; chart.cursor.events.on("cursorpositionchanged", function(ev) { var xAxis = ev.target.chart.xAxes.getIndex(0); var yAxis = ev.target.chart.yAxes.getIndex(0); cursorPosition.x = xAxis.positionToDate(xAxis.toAxisPosition(ev.target.xPosition)); cursorPosition.y = yAxis.positionToValue(yAxis.toAxisPosition(ev.target.yPosition)); });
{ // ... "cursor": { "events": { "cursorpositionchanged": function(ev) { var xAxis = ev.target.chart.xAxes.getIndex(0); var yAxis = ev.target.chart.yAxes.getIndex(0); cursorPosition.x = xAxis.positionToDate(xAxis.toAxisPosition(ev.target.xPosition)); cursorPosition.y = yAxis.positionToValue(yAxis.toAxisPosition(ev.target.yPosition)); } } } }
As simple, as that.
Using tracked info
Obviously, just tracking stuff is not useful in itself. Let's put it to good use.
Let's do something when user clicks on the plot area.
To register click/tap on a XY chart's plot area, we can use hit
event on chart's plotContainer
:
chart.plotContainer.events.on("hit", function(ev) { console.log("Clicked on", cursorPosition); });
chart.plotContainer.events.on("hit", function(ev) { console.log("Clicked on", cursorPosition); });
{ // ... "plotContainer": { "events": { "hit": function(ev) { console.log("Clicked on", cursorPosition); } } } }
Now, whenever user clicks anywhere within plot area, we'll get coordinates of that event, expressed in real values.
Here's the full working chart:
See the Pen amCharts V4: Tracking cursor position in axes by amCharts (@amcharts) on CodePen.24419