fperucic / treant-js

Treant.js - javascript library for drawing tree diagrams
MIT License
860 stars 314 forks source link

Rendering of large trees is slow (1000+ nodes) #93

Open pconnell99 opened 6 years ago

pconnell99 commented 6 years ago

I have a tree with over 1000 nodes and it is taking a long time to render (45+ seconds). I've tried a few things to tidy that up (the browser layout stage is taking most of the time) but they are not quite working.

As I see it, every node is added to the display and the browser re-renders the layout (time-consuming). If I set the treant-id to display:hidden then back after the render, it is 6 seconds!

However, the render is squashed up into a tiny div! I'm guessing that because the div is hidden during render it has 0 height/width and you are depending on those to render properly.

I had the idea of trying to render to an unattached DOM element then attaching it, but it did not like that as it needs the #treant-id element to be in the DOM to work.

Would an enhancement be to render the tree to an unattached DOM element before passing it back to the DOM? I'm looking in the codebase now for an opportunity to do this.

Any other ideas for speedups?

dlgoodchild commented 6 years ago

I have similar issues; painfully so. I'm investigating alternative processing and also thinking about lazy loading... it might be a while arriving unfortunately. I might make an experimental branch and publish it... I'll let you know.

pconnell99 commented 6 years ago

The hide/show worked well but for the display anomalies. If you render to an unattached div then attach it at the end of the render would that work?

let treantdiv = document.createElement('div').id = 'treant-id'; and then work on that div, finally attaching it to the DOM at the end of the render maybe. Just trying to find a solution.

If it's not IN the dom the browser won't rerender the layout for each node, then just do one update for the whole tree at the end. Sort of double-buffering kind of thing.

mpil2 commented 4 years ago

It is old, but maybe someone could find it handy. In Treant.js, function var Tree = function (jsonConfig, treeId ) - this.reset. I use: this.drawArea = document.createDocumentFragment(); so drawing is into memory, without bunch of reflows. I add this fragment to DOM at the and of this function, just before this._R = new Raphael( this.drawArea, 100, 100 );.

      var frag = this.drawArea;
      this.drawArea = this.testArea;
      this.drawArea.appendChild(frag);

For size computing, you also need to remember this.CONFIG.container and give it also class Treant, e.g. something like:

this.testArea = UTIL.findEl( this.CONFIG.container, true );
UTIL.addClass( this.testArea, 'Treant' );

Then, in TreeNode.prototype.createGeometry, you need to use this testArea for computing width and height of the node. E.g. at first compute size using testArea and then move it to the fragment:

        tree.testArea.appendChild(node);
        this.width = node.offsetWidth;
        this.height = node.offsetHeight;
        node.remove();
        drawArea.appendChild(node);

So it computes only small div and does not need to reflow always whole tree, which is very time consuming and increases exponentially.

Another improvement is in function positionNodes, there is "for" loop with this.drawArea.clientWidth and this.drawArea.clientHeight used inside. Better is remember these values before loop and use these remembered values.

I cannot say, if this has some display anomalies in some situations, but for me, it improved rendering speed drastically and my trees look ok.

yinxs commented 4 years ago

That solution works fine in Chromium based browsers here, but in Firefox everything turned out all wrong. Connectors were drawn away from the nodes, and outside of the divs the were supposed to be drawn into. Nodes couldn't be interacted with anymore. Maybe it's something small, but JS is not my strong suit. Too bad, because with around 1500 nodes, speed could definitely use a boost!

mpil2 commented 4 years ago

That solution works fine in Chromium based browsers here, but in Firefox everything turned out all wrong. Connectors were drawn away from the nodes, and outside of the divs the were supposed to be drawn into. Nodes couldn't be interacted with anymore. Maybe it's something small, but JS is not my strong suit. Too bad, because with around 1500 nodes, speed could definitely use a boost!

Sorry for confusing, my description was slightly misleading, drawing lines in reset function, i.e. line this._R = new Raphael( this.drawArea, 100, 100 ); has to be as the last, just before "return this". Lines has to be drawn to the real document, not fragment. Original description fixed.

yinxs commented 4 years ago

Lines has to be drawn to the real document, not fragment. Original description fixed.

Thanks! I implemented all your suggested fixes and it works like a charm. It's now blazing fast in comparison. I even got it to work in IE (the organization I work for just won't let that one go) by changing node.remove(); to tree.testArea.removeChild(node); although I hope that was the correct way to do it. Anyway, great fix and thanks again!

mpil2 commented 4 years ago

Great. Oh yes, node.remove() is supported from IE12, not before, your adjustment is of course ok.

gerasha2 commented 3 years ago

@mpil2 @yinxs Im not familiar with javascript. Do you mind provide me or post modified code here? I have over 1000 rows and rendering takes more than a minute. Thanks a lot.

mpil2 commented 3 years ago

@gerasha2 Here you go, just unzip and replace this file, it should be a lot better then Treant.zip

gerasha2 commented 3 years ago

@mpil2 Thank you! Performance improved when its up to 5 layers dip, but adding an extra layer chocking UI again. I noticed that scroll bar are missing when an expanded object goes outside of window.

mpil2 commented 3 years ago

@mpil2 Thank you! Performance improved when its up to 5 layers dip, but adding an extra layer chocking UI again. I noticed that scroll bar are missing when an expanded object goes outside of window.

What layers? In case you draw multiple things over each other, the demands could really increase rapidly. For me, one tree with many hundreds of nodes displays in just a few seconds. For scrollbars - insert whole tree into div with style "overflow:auto", "position: relative", and some given width and height, e.g. 100%. For me the scrollbars inside the tree never worked ok, this was easier and works quite good.

knutesears commented 6 months ago

@gerasha2 Here you go, just unzip and replace this file, it should be a lot better then Treant.zip

A thousand thank-yous -- I was not looking forward to digging into the performance issues, or looking for a new library, or, god forbid, trying to write my own. I suppose I could have started with this and stripped out the large amount of stuff I don't need, but I still saw that as a days long project. Hell, it can take days to find the best library/module for the task at hand if there are lots and the task is complex.

Why has this not been added to the code base? Is anyone at the wheel?