-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: html elements #124
base: master
Are you sure you want to change the base?
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 :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
createElement
to d3 calls. transitionDuration
andtransitionEase
functions added back
@@ -27,6 +22,44 @@ export default function () { | |||
var searchSum = 0 | |||
var totalValue = 0 | |||
var maxDelta = 0 | |||
var p = partition() | |||
|
|||
const containerElement = document.createElement('div') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't want to do it either, but didn't find any other way...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't think of a solution right now, that's why I suggested taking a separate PR to deal with it.
function setSearchDetails () { | ||
detailsElement.innerHTML = searchSum + ' of ' + totalValue + ' samples ( ' + format('.3f')(100 * (searchSum / totalValue), 3) + '%)' | ||
} | ||
|
||
var classMapper = function (d, base) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use chain here too. .style('display', 'none')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chains are slower. They loop over collection on each chain "link" and do other unnecessary things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same.
return chart | ||
} | ||
|
||
chart.transitionEase = function (_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.