vasturiano / force-graph

Force-directed graph rendered on HTML5 canvas
https://vasturiano.github.io/force-graph/example/directional-links-particles/
MIT License
1.61k stars 248 forks source link

Challenges implementing force graph zoom functionality #166

Closed aoloo closed 3 years ago

aoloo commented 3 years ago

Is your feature request related to a problem? Please describe. I am new to force graph and I do not understand how to utilize zoom() zoomToFit() centerAt() programatically for my usecase. This is what I am trying to achieve. I would like to set a default zoom level and have pan functioanlity for canvas navigation.

Describe the solution you'd like Two possible solution for me would be either 1) Default or static zoom level (Preffered) 2) Or disable zoom and have pan functionality enabled.

Describe alternatives you've considered I tried using enableZoomPanInteractio() but that disabled the pan functioanlity which I need to be enabled. I went to various external sites for guidance on a solution https://stackoverflow.com/questions/16178366/d3-js-set-initial-zoom-level and https://bl.ocks.org/puzzler10/4438752bb93f45dc5ad5214efaa12e4a. I am unable to find a solution for my usecase.

Additional context This is the zoom level I would like to see on the screen. image

But the nodes automatically get zoomed in on the canvas. image

vasturiano commented 3 years ago

@aoloo thanks for reaching out.

You should just need to call .zoom(<level>) after component initialisation. And just in case, it's recommended to do that asynchronously, to make sure it gets applied the graph initiates. So something like:

// your graph initialization
const myGraph = ForceGraph();
myGraph(<myDOMElement>)
    .graphData(<myData>);

// apply zoom
setTimeout( () => myGraph.zoom(5) );

Let me know if that solves the issue for you.

aoloo commented 3 years ago

@vasturiano Calling zoom within on onZoom causes this error in the console

setTimeout(() => { Graph.onZoom(() => { Graph.zoom(3.5); }) });

  Uncaught RangeError: Maximum call stack size exceeded
    at HTMLCanvasElement.<anonymous> (property.js:14)
    at Selection.each (each.js:5)
    at Selection.property (property.js:23)
    at Function../node_modules/d3-zoom/src/zoom.js.__webpack_exports__.default.zoom.transform (zoom.js:87)
    at Function../node_modules/d3-zoom/src/zoom.js.__webpack_exports__.default.zoom.scaleTo (zoom.js:110)
    at setZoom (force-graph.module.js:1285)
    at Function.zoom (force-graph.module.js:1262)
    at Function.comp.<computed> [as zoom] (kapsule.module.js:159)
    at Object.onZoom (force-graph.js:159)
    at HTMLCanvasElement.<anonymous> (force-graph.module.js:1481)
Alexithemia commented 3 years ago

@aoloo maybe a event.preventDefault() or event.stopPropagation() on the component for 'onwheel' events to stop zooming? and setup an initial zoom when the graph has loaded with graph.onEngineStop(fn)

aoloo commented 3 years ago

@Alexithemia I have tried your suggestion and It is still not working.

Initial setup as suggested by @vasturiano setTimeout(() => Graph.zoom(3.5));

event listener for onwheel || wheel event d3.select("#graph").on("onwheel", (e) => e.preventDefault()); it seems like wheelevent does not get triggered . I also tried using my touchpad on my lap with two finger gestures and wheel does not get triggered. mouseover mouseup mousedown are able to be triggered, but wheel

I am also using force-graph api to set the zoom level Graph.onZoom(() => { setTimeout(() => { Graph.zoom([3.5]); }); });

It seems the zoom level gets set, but when using two fingers gestures on touchpad and mouse wheel causes the nodes to jump and move up and down on the canvas. Not sure how to disable that and have the panning functionality enabled at the current 3.5 zoom level

apologizes for any inconvenience, I truly appreciate the help and guidance so far.

vasturiano commented 3 years ago

@aoloo calling zoom within onZoom is causing you an infinite loop. Because one triggers the other and vice versa.

Can you describe your intention in doing so?

If you just wish to zoom into a particular level, invoking .zoom should be sufficient.

aoloo commented 3 years ago

@vasturiano thank you for getting back to me. apologzies for not being more clear.

during graph initialization I call the function bellow to set the initial zoom level

setTimeout(() => {
    Graph.zoom(3.5);
  });

What I am trying to achieve is setting Max and Min Zoom level. For example I do not want the user be able to zoom out this far. image

Or Be able to zoom in this much image

I understand you can call the .onZoom event, but what is the best way to set the Min and Max zoom level when the user zooms in and out? Again apolozies for any confusion still new to d3. Thank you!

vasturiano commented 3 years ago

Thanks @aoloo for the clarification, I see it now.

The short answer however is that this is not currently possible to modify.

The min/max zoom level is configured using d3-zoom's scaleExtent attribute. This is currently being hardcoded into the internal chart zoom component state here.

This state is not accessible outside of the component closure, so you won't be able to modify it from your app. That is not without modifying this component's source.

But taking a step back, could you describe why it's important in your use case to set different values for the zoom extent?

aoloo commented 3 years ago

@vasturiano thank you for the response. Would it possible for you to include that feature? The ability to set Min/Max zoom levels could benefit the community in the near future as it becomes more used. I see future use cases of needing the ability to access zoom level state. I do not want to get into the detail of my use case. Having the ability to set force-graph min/max zoom levels would enhance graph navigation experience for our users. I hope I was able to answer your question. Thank you for the handwork.

vasturiano commented 3 years ago

@aoloo I finally got around to exposing the minZoom and maxZoom methods. They are now available from v1.39.0.

You can use it like this:

myGraph
  .minZoom(0.1)
  .maxZoom(100);

And thanks again for your feature request.

aoloo commented 3 years ago

@vasturiano Thank you so much!