refactor: html elements#124
Conversation
This is a large change that ripples through many parts of the project. Motivation behind this change: - Desire for faster and smoother animations - Shorter page load times - Shorter zoom in/out delay - Make nodes text selectable - Make tooltip text selectable This was achieved via following changes: - Switch from svg to HTML elements. This simplifies tree structure and speeds up layout and rendering (each svg group had `rect` and `foreignObject` with `div` inside, now we have just a single `div` for each node). - Switch from d3 animations to CSS transitions (smoother, less overhead) - Hold `Shift` while tooltip is up to keep it on screen (allows to select tooltip text) - Don't activate `zoom()` when text is selected on page (allows to select node text) - Use CSS style for search highlight and default to border color change (preserves background color that reflects regressions) - Adaptive tooltip positioning (allows larger tooltips and makes sure that entire tooltip is visible in browser's client area) - Don't use ideomatic d3 attr setters (while nice looking, they are slower than more conventional approach) Conflicts: dist/d3-flamegraph.js dist/d3-flamegraph.min.js
Conflicts: dist/d3-flamegraph.min.js
Otherwise zoom-in will go out of container bounds. Conflicts: dist/d3-flamegraph.min.js
This way flamegraph will be as wide as containing element. E.g. you can resize browser window, call update() and flamegraph will fill available space. Conflicts: dist/d3-flamegraph.min.js
Conflicts: dist/d3-flamegraph.min.js
Conflicts: dist/d3-flamegraph.min.js
Conflicts: dist/d3-flamegraph.min.js
|
@wonder-mice that's the large one. Might need a bit more time to review. After this, it's just a simple function rename PR that should be merged straight away. |
|
Without looking into the code too much yet, with my not-so-large example profiles, this can be 2x faster than current |
|
Colors are darker because I removed transparency to make blending easier for browser. Color mapper should care about that, imho. That said, I talked with WebKit folks and they said it doesn’t really matters, since it always does alpha blending regardless. Still think opacity by default is bad :) |
spiermar
left a comment
There was a problem hiding this comment.
Overall, the bulk of the improvement is coming from not using SVG elements. My comments are as follow:
- set opacity back to default. Ok with having an option to disable it.
- highlight the background of the frame instead of the border. Ok with configurable color.
- liked the new tooltips, specially removing a dependency. I would move the tooltip portion of the PR to a separate PR, specially because of the document event listeners.
- update the d3 calls to use chaining.
- move the initial
createElementto d3 calls. transitionDurationandtransitionEasefunctions added back
| var maxDelta = 0 | ||
| var p = partition() | ||
|
|
||
| const containerElement = document.createElement('div') |
There was a problem hiding this comment.
This is an anti-pattern on d3 plugins. Usually you would want to use something similar to .append('div') on .enter().
| containerElement.appendChild(titleElement) | ||
| containerElement.appendChild(nodesSpaceElement) | ||
|
|
||
| const externalState = { |
There was a problem hiding this comment.
This is being used keep the tooltip in the same place and selecting text only, correct? If that's the case, I would try to move that to a separate feature pull request. Not a big fan of adding document event listeners and handlers in the plugin. There might be a way of keeping the key state in an external object.
There was a problem hiding this comment.
Yeah, I didn't want to do it either, but didn't find any other way...
There was a problem hiding this comment.
I can't think of a solution right now, that's why I suggested taking a separate PR to deal with it.
| detailsElement.innerHTML = searchSum + ' of ' + totalValue + ' samples ( ' + format('.3f')(100 * (searchSum / totalValue), 3) + '%)' | ||
| } | ||
|
|
||
| var classMapper = function (d, base) { |
There was a problem hiding this comment.
Can probably use multiple .attr("class", d) in the d3 chain.
| } | ||
|
|
||
| var colorMapper = function (d) { | ||
| return d.highlight ? '#E600E6' : colorHash(getName(d), getLibtype(d), getDelta(d)) |
There was a problem hiding this comment.
Setting a class and using CSS to set the color of the highlights. Is there a reason why the CSS only sets the border color and not the background color?
There was a problem hiding this comment.
I choose such CSS style because I wanted to preserve bg color. I only use it in differential mode and in this mode colors are very important.
There was a problem hiding this comment.
I just find it a bit hard to visualize the highlighted frames with just a different border color.
|
|
||
| // EXIT old elements not present in new data. | ||
| g.exit().each(function (d) { | ||
| this.style.display = 'none' |
There was a problem hiding this comment.
Could use chain here too. .style('display', 'none')
There was a problem hiding this comment.
Chains are slower. They loop over collection on each chain "link" and do other unnecessary things.
There was a problem hiding this comment.
Overhead was never significant, but I can profile again.
| // UPDATE old elements present in new data. | ||
| g.each(function (d) { | ||
| const wpx = width(d) | ||
| this.className = classMapper(d, wpx < 35 ? 'node-sm' : 'node') |
| return chart | ||
| } | ||
|
|
||
| chart.transitionEase = function (_) { |
There was a problem hiding this comment.
Some users depend on these functions. I'm Ok with not having it as a default, but option should be there.
There was a problem hiding this comment.
Well, problem is that now transitions are CSS based and I don't see how to keep these functions "functional", e.i. I can see how we can keep them, but they will not work.
There was a problem hiding this comment.
We have a great D3 guy here in the office, so I'll ping him see if he has any ideas.
|
I realized my mistake. Looks like your intention was to be a d3 plugin. But I was looking at it as flamegraph builder that just happened to use d3 because it was easier at some point. I have some pending patches that add very interesting functionality (better comparison mode), but they have d3 completely removed (because I didn't find how to do it with d3 efficiently and the only part of d3 still in use was node match-by-id-reuse machinery). I surely share the patches later, maybe you'll figure out how to do it in d3 if you find them interesting enough. |
Not particularly attached to d3, but I've built other pure JavaScript visualizations in the past and quickly things get out of hand, and I've found d3 to be easier to maintain in the long run. I'll try to get all these changes in first, then we can chat about the other changes. |
No description provided.