Skip to content
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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

refactor: html elements #124

wants to merge 8 commits into from

Conversation

spiermar
Copy link
Owner

No description provided.

Andrey Pokrovskiy added 7 commits October 23, 2018 13:37
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
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
@spiermar
Copy link
Owner Author

spiermar commented Oct 23, 2018

@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.

@spiermar
Copy link
Owner Author

Without looking into the code too much yet, with my not-so-large example profiles, this can be 2x faster than current master. Having said that, it's a very different approach to rendering the data, and the styling needs to be improved a bit. The colors seem to be off, a bit darker, and the highlighted nodes are not easily visible.
I might cherry pick a couple other changes from this PR, like the tooltips, before reviewing the code in more detail.

@wonder-mice
Copy link

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 :)

Copy link
Owner Author

@spiermar spiermar left a 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 and transitionEase 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')
Copy link
Owner Author

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 = {
Copy link
Owner Author

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.

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...

Copy link
Owner Author

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) {
Copy link
Owner Author

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))
Copy link
Owner Author

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?

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.

Copy link
Owner Author

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'
Copy link
Owner Author

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')

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.

Copy link
Owner Author

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')
Copy link
Owner Author

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 (_) {
Copy link
Owner Author

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.

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.

Copy link
Owner Author

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.

@wonder-mice
Copy link

wonder-mice commented Oct 24, 2018

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.

@spiermar
Copy link
Owner Author

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.
I can have a look in the patches and talk with a few guys here about possible d3 implementation ideas too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants