I have completed freeCodeCamp's d3.js course to learn d3.js, and now I am starting to mess with code examples to understand it better.
I found this example on codepen: https://codepen.io/geny/pen/XodLrp
Example of the graph
I can't seem to understand how to disable the automatic node movements when I drag one node. Just when I am leaving the left-click, 1 or 2 seconds later the node just falls back to the rest of them. What I want to to, is leave the existing animation as is, but when a user wants to interact with the graph, I want the "forces" pulling nodes towards other nodes to be disabled, so the user can arrange them as he likes.
Any suggestions, what am I missing?
I tried messing with the .charge and .gravity, but it does not seem to make it.
var force = d3.layout.force()
.nodes(nodes)
.links([])
.gravity(0.1)
.charge(-1000)
.size([w,h]);
Related
So I need to create a static directed graph that depicts the process of creating something. Its children can point to the same stage but doesn't have to. What's important is that in the end, all the children branches point to the same final stage.
Its final look if it comes to the structure should be something similar to the photo below
https://imgur.com/a/BXr44GO
So until now, I have experimented with the force layout graphs from d3. I have searched for examples and similar questions here, but none of them referred to my specific problem. However, I'm not an expert in d3, so maybe I was doing something wrong, or I missed something, that's why I'm coming here because nothing else comes to my mind.
Some of the examples:
https://bl.ocks.org/mbostock/3750558
https://bl.ocks.org/heybignick/3faf257bbbbc7743bb72310d03b86ee8
My main issue with the d3 force layout graphs is that I have no clue how to make them static and spam each time in the same position without floating around like they are on the examples.
So my question would be, is it possible to make the graph as provided in the image above with d3 force layouts, or should I just work on creating my custom layout that would present graph in the manner that I need.
If it's possible, I would kindly ask for a piece of advice on how should I proceed with this kind of graph.
I think that what you are looking for is d3-sankey, you can find more examples here
If you use force simulation. you will have to calculate the position of each node and 'fix' them like here and also work in a static force simulation, which is basically the same, but you control how many times the force is 'run', like here, take a look at the following lines:
// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
// Of course, don't run too long—you'll hang the page!
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();
I'm working mainly from Jim Vallandingham's Bubble Chart example and I have all the data linked to the circles and the force layout running normally when I first refresh the page. I want the bubbles to be able to split and group together similar to the code running here but after I click on the buttons to move the bubbles they all just overlap on each other. You can see the live version of what I have on JSFiddle. In Vallandingham's he writes that the main collision/bubbles not overlapping effect comes from the charge function:
function charge(d) {
return -Math.pow(d.radius/2, 2.0) / 8;
}
I used that in my code but for some reason it's not working the same way and I can't figure out why. I have also tried manual collision detection in different ways but I haven't been able to get it to work. Thanks in advance!
I am rendering an SVG using D3 that shows circles from JSON data. I want to support zooming and dragging. The JSON structure can get very large. Here is my main issue:
Appending circles for all JSON entries doesn't really work. The page becomes way too slow as there might be thousands of <circle> elements in the DOM.
How I'm solving it:
I keep a reduced copy of the data set that I update in the drag function. On each dragging event, I declare an empty data set:
var reducedData = [];
I go over the entire data set and only push to reducedData the circles that have center coordinates that are visible given the current axis. I then erase the SVG and redraw it using reducedData. I do the same process on every zoom event, only appending to reducedData the circles that have a radius greater than 5 pixels given the current zoom ratio.
Although page is very responsive and seems to work well, it's very inefficient and I'm sure this isn't the best way to do this. What are some alternative solutions to my problem? Thanks.
Of course there's always room for improvement, but I think that your approach is already good enough and may it doesn't get so much better than that. However here are some points for you to consider and/or test by yourself if you wish so...
First off I would recommend you to check if any optimization is really needed. In latest versions of Google Chrome, in its DevTools under the "performance" tab, you can use CPU throttling to simulate a slower device. Then using the timeline tool, you can verify if either your data reduction or DOM manipulations are causing any bottlenecking and dropping your frame rate. If not, don't sweat it, you're good to go.
If from your analysis, you find that the data reduction is slowing down your rendering, you can use the timeline tool to find exactly where it is slow and research for faster alternatives.
In the other hand if it is your DOM manipulation that is causing any trouble, make sure that you're using the general update pattern which ensures that you're creating or removing elements only when really needed. Furthermore you may speed up the creation of circles by duplicating them instead of creating new ones.
Usually when too many data items needs to be visualized, as a last resort we switch from SVG to a canvas based visualization, but I think that would be overkill for your context.
Hope it helps and let us know if you have any questions.
I ended up using Crossfilter.js for fast filtering of the data. That way I don't need to manually keep a reduced copy of the data set. I can simply filter it quickly on each drag and zoom event. Thank you for everyone that answered.
How I solved this issue was to only update visible svg elements as the user is panning/zooming.
function pointInDomain(d, domain) {
return d.x < domain[1] && d.x > domain[0]
}
function zoomed() {
xz = d3.event.transform.rescaleX(x);
xGroup.call(xAxis.scale(xz));
var domain = xz.domain();
clippedArea.selectAll("circle")
.style("visibility", d => pointInDomain(d, domain) ? "visible" : "hidden")
.filter(d => pointInDomain(d, domain))
.attr("cx", d => xz(d.x));
}
JSFiddle
I wanted to produce a visualization that contains a good deal of nodes with the d3 force layout (more than 500 hundred nodes). While it is working correctly with as much as 200 hundred nodes it gets very slow with about 500, in the sense that the layout hiccups from one frame to the next and events like mouseover on nodes are far from being responsive. This made me ask several questions.
Is there some kind of limit in the number of nodes after which it is not recommended to use the force layout ? If so, is there any other library that could handle the job ?
If I wanted to speed up the process with d3, which parts should be optimized ? I tried keeping the use of css/attributes markup minimal (just gave a radius and a fill color to nodes + stroke-width and stroke color to links) and reduce the use of interaction (mouseover events) but could there be any more optimization done to the force object which holds all of the information ? The size of data must play a certain role...
Thank you for your input !
One way of doing this would be to handle not every tick event, but only a fraction of them, e.g. skipping a specified number or dynamically adapting the number of events depending on other considerations.
If you want smooth movements, add a transition between the positions set in the handled tick events. You can of course combine these ideas as well and skip events while the transition is running, handling the first one after it has been completed.
I have a D3 setup using "Nodes" and "Lines". When the graph first appears, it bounces in with gravity until it settles in the middle. Does anyone know of a way to have it appear automatically in the middle without the "bounce" sort of effect?
P.S I am using force layout
Calling start resets the cooling parameter, alpha; alpha decays exponentially as the layout converges on its solution, and then stops so as to avoid wasting the cpu. There's no jittering on start (other than coincident nodes, which is necessary to avoid a divide by zero). However, anytime you have conflicting forces and geometric constraints (links), it's natural to expect the layout to adjust when starting.
If you want to avoid this bounce, you either need to keep the graph permanently hot (say by calling d3.timer(function() { force.resume(); })) or you'd need to do something else, like adjust the alpha parameter manually to reheat gradually instead of instantaneously.
Edit: In 2.8.x, you can avoid the first bounce entirely by running the force layout synchronously on startup. For example: http://bl.ocks.org/1667139
Another strategy I've used before is to gradually increase the radii of each node from zero over the first, say, 50 or 100 force ticks. You can see what that looks like (in Protovis, but it would behave the same way in d3) in the Dorling cartograms on the one.org data site.