I am handling a large dataset to render for D3 force-layout, but realised that the performance suffers a lot using SVG. Read that rendering in canvas is much better, so am trying to understand it now.
One of the functions I need to code for is the addition of new nodes & links in the existing graph (without a refresh), as well as new links. Is there any hack that can be done to do this? Since canvas doesn't have DOM structure like SVG for me to select and update...
Am referencing to this canvas force-layout create using d3v4.
https://bl.ocks.org/mbostock/ad70335eeef6d167bc36fd3c04378048
Thanks!
There's a really good post that someone put together about using canvas called Working with D3.js and Canvas.
In short I'd recommend doing some data-binding into some dummy HTML, and using the results of this to render your output.
First create a fake DOM element that you can use
const fakeContainer = d3.select(document.createElement("custom"));
Now data-bind to it. Note, only create it the once, re-use it for re-renders.
const join = fakeContainer
.selectAll("circle")
.data(myData);
join.exit().remove();
join.enter().append("circle");
Then when it comes to rendering:
fakeContainer.each(function(d) {
// Render a canvas circle
});
Related
I'm trying to draw an axis on a canvas using D3 functionality (as exampled here https://www.tutorialsteacher.com/d3js/axes-in-d3). However every example I've seen is using an SVG while I wish to use canvas. On the other hand I didn't see any indication, in their documentation or elsewhere, that it can't be done in canvas.
Is it possible? If so, how?
In short: no, that's not possible.
D3 is pretty much render agnostic, meaning it can be used to create SVG, Canvas, other HTML elements etc. However, some modules are indeed quite specific, and that's the case of d3-axis.
If you have a look at d3-axis source code you'll see that it append SVG <path>, <line> and <text> elements for creating the axis. For instance:
path.enter().insert("path", ".tick")
Finally, here you have a discussion on this subject, where Bostock (D3 creator) abandons the idea of modifying the d3-axis module for creating axes on HTML canvas.
I'm backend developer for several years but a newbie in frontend issues.
I used "graphviz" (using d3.js) to draw an SVG graph from DOT notation.
Everything is working fine but one thing I don't get in my mind:
If I "open" another (or the same one) graph its starting position is the
same as this from the previous drawn graph even if I completely remove
the whole node content from the dom as follows:
var svg = d3.selectAll("svg");
var otherBelow = svg.selectAll("*");
otherBelow.remove();
// svg.remove();
Doing this and checking the page source the nodes below SVG are realy dropped
but drawing the new graph it has exactly the position of the previously
moved graph in "transform" attribute. Doing a work around by resetting the
position bevore solves this problem but then the problem remains for the
"moving on mousedown" capability. Then the graph immediately "jumps" to the old
ones position. But therefor I can't even get an information about somewhere
in the page source. Really the generated page code is 100% the same (with
diff tool) but has a different behaviour. Don't understand how this is possible.
So now my question: Is there a kind of caching? Or is there perhaps the
browser cache used somehow internally? How to fix this?
P.s. if I remove the SVG node itself I get a completely courious behaviour.
Then the newly drawn graph is not movable at all.
This (ugly) workaround does it for me.
// Snipped to render a new graph and reset its position
// #graph -> id of d3-graphviz div-container
document.getElementById('graph').innerHTML = ''
setTimeout(() => {
d3.select("#graph").graphviz()
.dot(yourDotData)
.render()
}, 50)
Explanation/Assumption why this works:
It seems that the deletion of the old graph with .innerHTML = ''and the creation of the new one should not happen in the same rendering phase/at the same time, therefore the timeout function.
The second part of the workaround is to use a new graphivz-instance to render the new graph, therefore the
d3.select(...) within the timeout function.
The graphviz renderer is still present on the element it was created on and has all its data still intact.
Removing the svg and then reuse the renderer is not a valid use case. Nico's answer is probably correct, but there are better ways to do it, but in order to tell you how you should do it instead I would need so see all of your code so I can understand what you really want to do.
I am trying to modify Bullet Charts example of dimple.js with each bullet chart being in a child-svg of the parent-svg. Purpose of having individual svg for each bullet chart is to make their management (show/hide/remove) easier. Also, this makes the recursive definition of a chart complete - That is, a chart is contained by an svg.
The fiddle for the modified version is here....
As you can see, from 2nd chart onwards, on mouse hover, tool tips go out of place!!! Please note that, for child-svg, I've set the style overflow: visible without which tool-tips were not visible at all.
Want to know if I am missing anything in handling the attributes of child-svg elements or is it a bug in dimple.js. Also, please let me know if you know of any workaround.
Thanks.
One of the first questions I have is why do you want child svg elements? What are you trying to accomplish?
The only difference I see in your code and the example is the height / width swap at the top and the sub svg + bounds.
Keep in mind that the origin changes with each sub-svg. This might be why you are having trouble with the tool-tips. Maybe you have that worked into your add-bullet calls.
I think nagu has the right approach here if you really want separate svg elements.
I am trying to get my head around d3 in its current version 4. Specifically: I a trying to create the x axis for a simple line chart in react and es6.
I have seen the examples of Mike Bostock and how he does it:
svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
But that is neither react nor ES6.
On another site I have seen the following variant:
renderAxis() {
var node = this.refs.axis;
var axis = d3.svg.axis().orient(this.props.orient).ticks(5).scale(this.props.scale);
d3.select(node).call(axis);
}
render() {
return <g className="axis" ref="axis" transform={this.props.translate}></g>
}
This is react and ES6 but not d3 in version 4.
I could try to adopt the version 3 code to version 4 of d3 but: The d3.select bothers me extremely. I don't want to make DOM-calls when I am using react (or some other library within, like d3). React should be used to render into the DOM in the most efficient way, not to get me DOM nodes.
So, my question is:
What is the react-way to create me a simple x axis? Or, if there is yet not such an answer: What is the react way to adopt the quoted code of Mike Bostock?
After a couple years of trying to figure out d3 in React, this is the best way my team and I have found of doing axes:
const Axis = props => {
const axisRef = axis => {
axis && props.axisCreator(select(axis));
};
return <g className={props.className} ref={axisRef} />;
};
Notes
axisCreator is the function returned by axisBottom, axisLeft, etc. It is created outside the component to expose to the user the full power of the d3-axis library.
className allows the user to style the axis however he or she wants.
This example uses refs, which the React docs caution against, but it uses refs to integrate with a third-party DOM library, which is one of the reasons to use refs that the React docs call out specifically.
It is understandable that using d3's select bothers you, but it's required if you want to let d3 manipulate the DOM, and if you don't let d3 manipulate the DOM, you lose out on all the functionality of d3 (which is an incredibly cool library).
If you are using React then you are pretty much giving control of how the DOM works to React because of it's internal workings. React will render your components inside a virtual DOM tree and then figure out the difference between the DOM tree in the page were you inserted the root component and the former. It will apply the difference between the two trees so that the tree in the page will look like the virtual one.
Mixing d3 and React requires a bit of a trick. All the elements you use d3 for (nodes or attributes) should not be owned by React. Otherwise weird stuff happens. Let's say your transform attribute for the g.axis node is set by d3, that implies that you don't render it in React. You let your d3 logic have exclusive ownership of it.
Now comes the next step. If you want to use other tools to describe DOM nodes then you got to put that logic inside componentWillMount and componentDidUpdate. Basically you only render the g element inside the render() method of React and then inside those two React lifecycle handles you can change the attributes of the g element and what's inside.
I will point you towards this post that goes a bit more in detail about the hows, but I would also like to give my two cents. From my perspective and experience with these two it is best to only use d3 for it's helper functions (generic math functions). Let the rendering logic (DOM composition) be done purely in React even though there's code duplication that arises. It is way easier to maintain code that is consistent and that doesn't have two different approaches to rendering mixed together.
TL;DR; Either give attributes or nodes ownership to either React or d3 for them to work together; or my recommendation, make your own axis component in React that outputs the same DOM elements as the d3 one (or different if you desire other functionality or style) and don't use d3 for rendering.
Hope I helped.
The code for rendering axis using React directly, rather than d3's select API, is actually not that complex. react-d3-axis provides a pretty simple, ~100 loc implementation. Going this approach lets you use d3 for stuff like data transformations and scale interpolation, and use React for rendering.
I'm setting up an experimental html5 website using canvas.
I am drawing 3 circles all next to each other and all I want to know is how to be able to select them.
I'd like them to become links, in a way. Not tags, since everything's gonna be created using javascript.
Something like kinetic JS : http://www.kineticjs.com/, but without the extra library.
I have found some scripts that are using ghost canvas and contexts, but the examples are for dragging and stuff. I only want to be able to select my shape and execute some code.
Thank you!
I am thinking you might want to look into the IsPointInPath() method. It will help you figure out whether or not the mouse clicked on your canvas object.
See Detect mouseover of certain points within an HTML canvas?
if you are talented in xml i suggest you to use canvas + SVG (http://www.w3schools.com/svg/)
And follow this simple example.
http://jsvectoreditor.googlecode.com/svn/trunk/index.html
regarding to SVG and Canvas , the differences are obvious, as you can load bitmaps in SVG, and you can draw lines using the canvas API. However, creating the image may be easier using one technology over the other, depending on whether your graphic is mainly line-based or more image-like.