🏠 back to Observable

Having some trouble with D3 dragging

I can’t for the life of me figure out what the difference is between @jrus/arrowheads and @jrus/bezier-segment which is causing the former one to only return positions relative to the mousedown location in d3.drag instead of returning positions relative to the SVG container’s coordinate system, the way the latter notebook’s plots do.

I thought I was copying code directly from one to the other.

I’ll give this another try tomorrow, but if someone more experienced can figure it out from a minute or two of inspection I would appreciate the help.

The difference is the coordinate system. The bezier-segment notebook is just using pixels, while the arrowheads notebook is using x and y linear scales. So, to fix the arrowheads notebook, you need to change your drag.subject definition like so:

d3.drag()
    .subject(d => ({x: x(d[0]), y: y(d[1])}))
    .on("drag", dragged)

This will now be symmetric with the x.invert and y.invert calls during the drag event.

1 Like

Thanks a bunch Mike.

I guess I still don’t understand what d3.drag().subject does. I’ll take a closer look at the docs / code and run some experiments if I get some time tomorrow.

Okay, I get it… .subject determines the starting coordinates based on the thing being clicked, rather than based on the position on screen per se, so that motions stay relative to the mouse movement instead of having the object jump. I implemented something similar a decade ago, but the D3 one is probably simpler and much better debugged. :slight_smile:

I’ll have to spend some more time fiddling and thinking it through carefully. Getting all the edge cases in mouse interaction right is always the hardest part for me of any web UI project.

Anyway, for now, the basics seem to be working:

1 Like

Hi Mike,

thanks for the drag().subject tip, that helped a lot after hours of googling while trying to feed a custom .x and .y accessor to drag and force.

Is there such a thing for forces, too? in other words, can i change the .x and .y accessor, so that the force will be calculated based on some other keys, like .xff and .yff ?

Thanks.

Just realized that .subject is a documented d3.drag() function, sorry! This means probably no such approach with d3.forceSimulation() is currently possible.

You can pass an accessor function to d3.forceX (x.x) and d3.forceY (y.y) if you want to change how the target position for the positioning forces is computed. But you can’t change the properties that the simulation itself uses to record the node’s position and velocity, as these are read-write properties, not accessors. Those are documented here:

And are:

  • index - the node’s zero-based index into nodes
  • x - the node’s current x -position
  • y - the node’s current y -position
  • vx - the node’s current x -velocity
  • vy - the node’s current y -velocity
  • fx - the node’s fixed x -position
  • fy - the node’s fixed y -position
2 Likes