How do I make a movable point that changes a D3 graph?

I’m new to data vis and javascript, so this is probably a pretty basic question. I want to have a moveable point whose x and y coordinates are 2 parameters for a graphed curved. Every frame the point is dragged, I want the curve to recalculate the line, but remain the same object. It would also be really nice for a curve to be automatically made for every point you start with, so you could define a set of points and automatically make a matching curve for each point.

Here is a link to my project:

You can see the point moves if you use the sliders, but I want to be able to click and drag the point to do the same thing.

Hi Lao!

I sent a suggestion to your notebook that shows how to programmatically control an observable input. Take a look at this “Synchronized Inputs” notebook for more information: Synchronized Inputs / Observable / Observable

The main idea is to set the viewof expected.value and viewof gamma.value and then dispatch the “input” events for them.

So your dragged function would look roughly like this (though it’d make sense to DRY this up using a separate set function the way it is recommended in the Synchronized inputs notebook):

function dragged(event, d) {
  d3.select(this)
    .attr("cx", () => {
      d.x = Math.max(0.001 + padding, Math.min(w - padding - 0.001, event.x));
      // Set the value of an input and then dispatch the "input" event
      viewof expected.value = xScaleInverse(d.x);
      viewof expected.dispatchEvent(new Event("input"));
      return d.x;
    })
    .attr("cy", () => {
      d.y = Math.max(0.001 + padding, Math.min(h - padding - 0.001, event.y));
      // Set the value of an input and then dispatch the "input" event
      viewof gamma.value = yScaleInverse(d.y);
      viewof gamma.dispatchEvent(new Event("input"));
      return d.y;
    });

  ///////////////////////
  update();
}
1 Like

Thanks for the suggestion. I will definitely look into synchronized inputs.

I tried the code, but when I go to drag the point, the graph updates and forgets that I’ve clicked it:

viewof expected.dispatchEvent(new Event("input"));

If I delete that, I can move the point, but the distribution does not change because the two sliders don’t update with the input.

I’m okay with not using sliders and handling everything within the one D3 cell. I was only using those sliders because I know how to implement them and I wanted to test the conversion of the (x,y) of the point into the (α,β) parameters that define the shape of the distribution.

Really, what I want to do is recalculate the function that graphs the curve based on the the position of the dragged point. For this part that makes the curve:

var g = svg.append('path')
    .datum(makeCurve(expected, gamma))  // input parameter for curve
    .attr("clip-path", "url(#chart-area)")
    ...

I want to recalculate the datum based on the (x,y) position of the point

.datum(makeCurve(xPositionOfPoint, yPositionOfPoint))

is there a way to do this without sliders?

Hi Lao,

There was another change in my suggestion (you should have received an email with the suggestion that allows you to merge in the changes) to replace the two slider inputs with the standard Inputs.range implementation. Did you see that part as well? I confirmed it was working (with a small y offset issue that I mentioned in the suggestion). You can see the forked version here (again with the y offset issue): Beta Distribution UI / Duane Millar Barlow / Observable

If that still doesn’t work for you, let me know!

1 Like

I did not see the email. What you send works great, thank you so much!

1 Like