-
-
Notifications
You must be signed in to change notification settings - Fork 388
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
Interactive canvas layer #922
Comments
i'm open to exploring this. i am sure updating one canvas element will be cheaper than updating 200 dom elements. i'd like to see some tests of how it impacts mem as well, not just cpu, in cases of 100 charts with 5 series each. or 200 sparklines with one series each besides .u-over, there is also .u-under, as well as plans to add a clipped and unclipped region in #876. taking all this into consideration makes this not completely trivial. long term, i would prefer not to have two different strategies either. |
Here is small demo. It takes time to render because there are a lot of series on each chart. https://minardil.github.io/uPlot/demos/lot-of-series.html. Changes in uplot are now dirty but I think it could be done as a plugin if there is an exposed method like u.draw(idx, opts: {canvasCtx, stroke, fill, width, etc}) |
thanks, i'll take a look soon. i did a quick test of the impact of creating multiple canvases and putting bunch of hover point dom elements inside. performance profile of moving the mouse (without moving the points) showed a lot of cost going to browser hit-testing, even though there are no hover css styles and no listeners attached to the points. i found a neat trick to reduce this by a lot: (the main perf cost is not this, tho. the main cost is the actual <!doctype html>
<html>
<head>
<title>many-can</title>
<style>
body {
margin: 0;
}
canvas {
background: pink;
}
.wrap {
position: relative;
display: inline-block;
margin: 8px;
}
.overlay {
width: 100%;
height: 100%;
position: absolute;
opacity: 0;
z-index: 100;
left: 0;
top: 0;
}
.pt {
top: 0;
left: 0;
position: absolute;
will-change: transform;
pointer-events: none;
}
</style>
</head>
<body>
<script>
let count = 30;
let series = 200;
let width = 800;
let height = 600;
let pxRatio = devicePixelRatio;
let ctxs = [];
for (let i = 0; i < count; i++) {
let can = document.createElement('canvas');
can.width = Math.ceil(width * pxRatio);
can.height = Math.ceil(height * pxRatio);
can.style.width = `${width}px`;
can.style.height = `${height}px`;
let ctx = can.getContext('2d');
ctxs.push(ctx);
let wrap = document.createElement('div');
wrap.className = 'wrap';
wrap.style.width = `${width}px`;
wrap.style.height = `${height}px`;
wrap.appendChild(can);
for (let i = 0; i < series; i++) {
let pt = document.createElement('div');
pt.className = 'pt';
pt.style.width = '5px';
pt.style.height = '5px';
pt.style.backgroundColor = '#'+(Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, '0');
pt.style.transform = `translate(${Math.round(Math.random() * width)}px, ${Math.round(Math.random() * height)}px)`;
wrap.appendChild(pt);
}
// dramatically reduces cost of browser hit-testing
// https://stackoverflow.com/questions/41830529/optimizing-native-hit-testing-of-dom-elements-chrome/51534641#51534641
let ovr = document.createElement('div');
ovr.className = 'overlay';
wrap.appendChild(ovr);
document.body.appendChild(wrap);
}
</script>
</body>
</html> |
Maybe you misunderstood me. The problem is not in hovering points but in hovering whole line or it's area. It's is shown in my example. Of course it is better to draw all hovering items on second canvas but bottleneck is line but not points |
oh, yeah i misunderstood :) point hover is also a huge cost when you have tons of datapoints (not just tons of series), and i guess def related to moving things off to an separate interaction canvas. due to the way that bands are constructed, and opacity/alpha can interact between series, im not sure that moving the focus rendering to another canvas can be done properly in the general case. i think what you're trying to accomplish can be done with existing hooks, and simply grabbing the cached Path2D objects from the needed maybe once hover point rendering is moved to its own canvas, you can draw the focused/cached path to that canvas in a setSeries hook. it might be necessary to noop the focus redraw in uPlot, though it may already be done when |
Your optimization does not work at all on my computer: Here is my approach with second canvas: |
What is more, I can't disable fill because it is a requirement to have it in real production |
with this many series it's a weird requirement, i gotta say. usually area charts are basically usesless beyond a few filled series, except maybe in stacked areas, but that sucks in a whole lot of other ways. can you try enabling out-of-process canvas rasterization (and other gpu tweaks) and see if it makes a difference? (yes, i know you can't make all users do this :) https://github.com/leeoniya/uPlot?tab=readme-ov-file#unclog-your-rendering-pipeline also, when i try https://minardil.github.io/uPlot/demos/focus-cursor.html it creates but does not use the extra canvas. for sure i dont want a bespoke API for this specific case -- the standard focus route would have to use this, and i think that might be a big lift / increase in complexity for little benefit. again, i think it's perfectly feasible to use uPlot's existing API to do what you're doing in your fork. i would suggest trying get that working as i describe above so we can see if there are some minor changes that might be needed in uPlot to get that strategy to work cleanly. |
It hangs even with fewer number of lines. I just put so many in order to make it easily visible for any kind of computer. Anyway, your suggest of using setSeries helped. Thanks a lot! |
|
Also, as I already said, chart with a lot of series hangs on my computer even without "fill" |
Maybe It's worth it to add an example of such plugin to your demos? |
maybe you have a really really weak GPU, or your OS/browser/drivers have some issues. i'm trying this on a Ryzen 7 integrated laptop GPU in Linux on a 4k display in Chrome and in Firefox with zero lag. can you try my code above with no points and no fill on another machine / os / browser? Peek.2024-02-06.16-27.mp4 |
I know from my colleges that uplot works perfectly on m2 without any performance hacks |
I have checked safari with your code. It works better but not well enough |
Screen.Recording.2024-02-07.at.00.36.42.mov |
your info isnt very detailed. you're showing a screenshot of your system specs which has two very different GPUs. i have no idea which one it is using. M2/apple silicon are almost supercomputers, and nothing is slow on them, so i can't get anything from that datapoint. i tried the no-points, no-fill variant in latest Chrome on my 2016-2017 desktop build and got 0 lag.
Intel HD630 is also ~2016 era, but weaker and integrated. still something seems wonky to be this slow. idk, maybe drivers issue. to understand what is going on, you really need to dig in and investigate. |
Hello. I am planning to fork uplot to make it possible to move hovering to second canvas in order to improve performance when there are a lot of (200 and more series. Maybe we could discuss it and support this functionality in the library? I am sure that it will improve the performance because we do it in our custom library
The text was updated successfully, but these errors were encountered: