0

I have recreated this index chart in Dash.

This is a gif of the chart in the link. o

The data is dynamically updated by a callback that listens to the hoverData property of the dcc.Graph component when the user hovers the mouse over the graph.

I also added a callback that disables/enables the updates. Right now it is triggered by clicking on the graph area, that is, a change in the clickData property.

However, this doesn’t feel very intuitive to me. I would like to enable the hover updates when the user holds the left mouse button down and drags the mouse, and disabling it when the mouse is released.

How can I implement this functionality?

guusto
  • 1
  • 1
  • What have you tried so far? – shrys Sep 17 '20 at 12:41
  • Honestly I don't know what to try. According to the [docs](https://dash.plotly.com/dash-core-components/graph#graph-properties) There are no ```dcc.Graph``` properties that store drag events. I suspect that I need to write custom JS, but I don't know for sure. – guusto Sep 17 '20 at 12:46

1 Answers1

0

Check out the following implementation, did not refer the api so there could be a better way to do it:

mousedown, mouseup, mousemove, SO answer for drag behavior

vis.add(pv.Panel)
  .events("all")
  .event("mousedown", function() {
    isMousedown = true;
  })
  .event("mouseup", function() {
    isMousedown = false;
  })
  .event("mousemove", function() {
    if (isMousedown) {
      idx = x.invert(vis.mouse().x) >> 0;
      update();
    }
  });

var data = [],
  fy = function(d) {
    return d.price
  }, // y-axis value
  fx = function(d) {
    return d.index
  }, // x-axis value
  ft = function() {
    return data[this.parent.index].ticker
  }, // label
  w = 730,
  h = 360,
  S = pv.max(pv.values(stocks), function(s) {
    return s.values.length
  }),
  idx = Math.floor(S / 2) - 1,
  x = pv.Scale.linear(0, S - 1).range(0, w),
  y = pv.Scale.linear(-1, 5).range(0, h),
  rescale = true;

/* Normalize the data according to an index point. */
var indexify = function(data, cols, idx) {
  return cols.map(function(c) {
    var v = data[c].values[idx];
    return {
      ticker: c,
      values: data[c].values.map(function(d, i) {
        return {
          index: i,
          price: ((d - v) / v)
        };
      })
    }
  });
};

/* Compute new index values, rescale if needed, and render. */
var update = function() {
  data = indexify(stocks, names, idx);
  if (rescale) {
    var min = pv.min(data.map(function(d) {
      return pv.min(d.values, fy)
    }));
    var max = pv.max(data.map(function(d) {
      return pv.max(d.values, fy)
    }));
  }
  y.domain(min, max).nice();
  vis.render();
}

/* The visualization panel. Stores the active index. */
var vis = new pv.Panel()
  .def("i", -1)
  .left(60)
  .right(70)
  .top(20.5)
  .bottom(18)
  .width(w)
  .height(h);

/* Horizontal gridlines showing %-change. */
vis.add(pv.Rule)
  .data(function() {
    return y.ticks(8)
  })
  .bottom(y)
  .strokeStyle(function(d) {
    return d == 0 ? "black" : "#cccccc"
  })
  .anchor("left").add(pv.Label)
  .text(function(d) {
    return (d * 100).toFixed(0) + "%"
  });

/* Y-axis label */
vis.add(pv.Label)
  .data(["Gain / Loss Factor"])
  .left(-45)
  .bottom(h / 2)
  .font("10pt Arial")
  .textAlign("center")
  .textAngle(-Math.PI / 2);

/* Stock lines. */
vis.add(pv.Panel)
  .data(function() {
    return data
  })
  .add(pv.Line)
  .data(function(d) {
    return d.values
  })
  .left(x.by(fx))
  .bottom(y.by(fy))
  .lineWidth(2)
  .add(pv.Label)
  .visible(function() {
    return this.index == S - 1
  })
  .textBaseline("middle")
  .textMargin(6)
  .text(ft);

/* Current index line. */
vis.add(pv.Rule)
  .visible(function() {
    return idx >= 0 && idx != vis.i()
  })
  .left(function() {
    return x(idx)
  })
  .top(-4)
  .bottom(-4)
  .strokeStyle("red")
  .anchor("bottom").add(pv.Label)
  .text(function() {
    return stocks.Date.values[idx]
  });

/* An invisible bar to capture events (without flickering). */
var isMousedown = false;
vis.add(pv.Panel)
  .events("all")
  .event("mousedown", function() {
    isMousedown = true;
  })
  .event("mouseup", function() {
    isMousedown = false;
  })
  .event("mousemove", function() {
    if (isMousedown) {
      idx = x.invert(vis.mouse().x) >> 0;
      update();
    }
  });

update();
#fig {
  width: 860px;
  height: 400px;
}
<script src="https://mbostock.github.io/protovis/protovis-r3.2.js"></script>
<script src="https://mbostock.github.io/protovis/ex/stocks.js"></script>
<link href="https://mbostock.github.io/protovis/style.css" rel="stylesheet" />

<div id="fig">
shrys
  • 5,860
  • 2
  • 21
  • 36
  • Thank you! The only problem is that the code you used is not from my chart - now I just need to figure out how to embed that into Plotly. – guusto Sep 17 '20 at 13:06