How to make 'shape' track mouse motion?

I have a requirement to provide a selection mechanism in one chart that will prompt a change in the data displayed in a related chart. The first chart is a smoothed x-y plot, let’s say covering an x range of 0 to 20, displayed in an element 800px wide. I have added an array of two rectangular shapes to the layout object of that chart. One of those should track the mouse motion, but snapping to integer values (i.e. when the mouse is at 4.87, the highlight will cover the interval from 4 to 5). A single click on the chart should lock the second highlight to the current position of the tracking highlight.

As far as I can tell, Plotly supports click and hover events but not motion. I don’t believe hover will help here, as the tracking is expected to work continuously, rather than only when in proximity of the the data. As a consequence, I’m now using jQuery motion events to get mouse coordinates within the SVG canvas. If I could get access to the underlying D3 scale object, I think I can map screen coordinates to data values. I would then need to use those values to update the shapes. I haven’t yet managed to confirm that that updating is a supported operation.

Is this possible? Am I approaching this the right way, or is there a less involved mechanism?

That’s correct. You might want to look at http://codepen.io/etpinard/pen/EyydEj for a sorta hacky way to track cursor position on a plotly graph. I think that should add you need for your project. Let me know if you have any other questions.

1 Like

Thanks etienne, that looks like exactly what I was needing (assuming I can successfully use .relayout() to move a shape).

The coordinates are not working after some basic action like zoom:

Because you have old reference of chart’s layout. On relayout the chart’s area changes its boundaries values, and you need its new values. You should to update this variables on relayout

  var xaxis = gd._fullLayout.xaxis;
  var yaxis = gd._fullLayout.yaxis;
  var l = gd._fullLayout.margin.l;
  var t = gd._fullLayout.margin.t;

how do i can do this in react ?

by using ref, gd is the just parent element of whole graph which will get created when you use , so just use ref and you should be able to access it.

That’s a perfect solution. If your gd is not placed at the left border of the page, rather use evt.offsetX and evt.offsetY as they refer to the mouse position relative to the gd’s top left edge.

Recently I struggled to to the same with geo plots, which have a different coordinate system. I wrote the following function to cover all (?) possibilities and also to clip events that occur outside of the plotting area:

function eventToCursor(event) {
    var layout = gd._fullLayout
    x = event.offsetX - layout.margin.l
    y = event.offsetY - layout.margin.t
    if (x < 0 || y < 0 || x > layout.width || y > layout.height) {
        return
    }
    
    if (layout.hasOwnProperty('xaxis')) {
        x = layout.xaxis.p2c(x)
        y = layout.yaxis.p2c(y)
        cursor = {cursor: {x: x, y: y}}
    } else {
        if (layout.hasOwnProperty('geo')) {
            geo = layout.geo
        } else if (layout.hasOwnProperty('mapbox')) {
            geo = layout.mapbox
        } else {
            return
        }
        x = geo._subplot.xaxis.p2c()
        y = geo._subplot.yaxis.p2c()
        cursor = {cursor: {lon: x, lat: y}}
    }
    return cursor
}