naver / billboard.js

📊 Re-usable, easy interface JavaScript chart library based on D3.js
https://naver.github.io/billboard.js/
MIT License
5.84k stars 352 forks source link

[FeatureRequest] onredraw hook #2204

Open adschm opened 3 years ago

adschm commented 3 years ago

I'm trying to implement a third-party mechanism for text overlap reduction in my code.

Currently, I'm just using onrendered to hook my function, which will have the label jump into position after each chart update. I would like to see a hook that is earlier in the process, e.g. something like the $redraw that appears to be only available for plugins ...

netil commented 3 years ago

it can't be achieved by onrendered option?

adschm commented 3 years ago

The repositioning is working, but the icons jump from their original position to the new one, which looks quite unprofessional:

https://jsfiddle.net/adschm/213wufbc/15/

I was hoping that redraw would be early enough to prevent this.

Off-Topic question: Is there any nicer solution available (something like the old d3.translate) to retrieve the existing location apart from the getTranslation function I use here?

adschm commented 3 years ago

Update: After looking at the fiddle again I see that onrendered appears to do the job for the first rendering, and only the update via .load() is the problem.

adschm commented 3 years ago

Update 2: load.done solves the issue for refresh:

https://jsfiddle.net/adschm/oda34ps2/3/

However, then I need another hook for the initial positioning. (Initial positioning also works if I simply call the function to reposition directly after bb.generate(), but IMO that's a race condition and only working "accidentally")

adschm commented 3 years ago

Update 3: the load.done approach breaks when arcs are deselected/hidden via clicking the corresponding legend item ...

adschm commented 3 years ago

I ended up with a combination of load().done and onrendered:

  1. The overlap treatment is boxed in a function preventCollision()
  2. onrendered calls that function. This will work for the initial plot and cover the various cases of updates, e.g. resize, on/off via legend, etc. automagically.
  3. For updates via "normal" load, the delayed start of onrendered would cause the icons to "jump into place" after maybe 0.5 secs (as described in the earlier posts). This can be healed by also calling preventCollision() in load.done, so the correct positions will be immediately visible after load. Of course, the function will then be triggered a second time in onrendered. However, since my algorithm is iterative, it will simply detect no overlap and exit after one iteration there (since everything has been done already during load.done).

This actually works quite well.

Still, providing an "onredraw" hook would improve the situation by not having to call the function twice on reload. While this appears to be cheap for me (at the moment), it might be more expensive for similar cases with other manipulation algorithms.