Closed leeoniya closed 4 years ago
...unless we also draw axes & labels on canvas, which might be good for perf since we don't need to touch dom. also might help with exporting images of chart.
function getLines(ctx, text, maxWidth) {
var words = text.split(" ");
var lines = [];
var currentLine = words[0];
for (var i = 1; i < words.length; i++) {
var word = words[i];
var width = ctx.measureText(currentLine + " " + word).width;
if (width < maxWidth) {
currentLine += " " + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
function getLinesForParagraphs(ctx, text, maxWidth) {
return text.split("\n").map(para =>
getLines(ctx, para, maxWidth)
).reduce([], (a, b) => a.concat(b));
}
simple text wrapping impl: https://stackoverflow.com/a/16599668
i'm currently prototyping this, which should bring some perf improvements due to less dom generated for labels & axes - 0 to be exact. this will keep the GC happier for data streaming and reduce mem usage when creating many plots at once. it also reduces the need for #71 and makes the story for #20 much better.
the html structure will be simplified and flattened, as will the CSS.
<div class="uplot">
<div class="title">Title</div>
<div class="plot">
<div class="under">below canvas stuff</div>
<canvas>plot, gutters & axes</canvas>
<div class="over">cursor, hover points, tooltips, selection</div>
</div>
<div class="legend">Legend</div>
</div>
dom layout demo:
https://codepen.io/leeoniya/pen/WNvwxBb?editors=1100
cc @ldstein @danyalejandro @CrashLaker @dgl @silverwind @stuck1233333
it's been a lot of work, but i've made good progress and the results are promising - tick alignment is now perfect and markers at plot extremes will not be cut off.
so far i've only validated basic rendering, cursor movement/zoom, axis & label placement. there's still work to do on testing resizing, multi-line tick labels, ensuring the APIs work as before and validating + updating all the demos (should be minimal).
right now the js code is a bit larger than the DOM version, but the css is smaller by the same amount. there's still a lot of DRYing left to do, too.
Does this mean that text is drawn statically on the canvas? If so, wouldn't that take away a lot of customization options regarding fonts and such?
I'd certainly prefer if ticks are drawn in the canvas because of mentioned padding issue, ideally configurable in their size.
Does this mean that text is drawn statically on the canvas? If so, wouldn't that take away a lot of customization options regarding fonts and such?
yes, i'll expose axis.font: {size, family, weight, stroke}
, but nothing fancier. there's also going to be a new axis.tick: {show, size, width, stroke, dash, gap}
- bascially grid
, plus .gap
(to shift the labels out).
I'd certainly prefer if ticks are drawn in the canvas because of mentioned padding issue
i lied. the issue with hover-point clipping is not fully resolved by this because the hover points are dom-based, and the way i currently hide them (and the cursor) it to give them neg coordinates (their container is overflow: hidden
) - which still clips its contents at its bbox. if i leave overflow: visible
, then it solves the issue (kind of) as long as i hide the cursor and hoverpoints with display: none
or something - but that's more state tracking and dom-fiddling than simple setting the position to off-screen. i need to think more about it.
i should have the canvas changes pushed in the next few days.
Question regarding the gray area behind the ticks in your screenshot above: Is that part of the canvas transparent or are you rendering a static background color on that strip? If static, it should be configurable.
the gray bg and outline on .uplot
and white bg on .under
are just set in css for this demo. i use it to visualize the bounds of everything to get the positioning & spacing right. there are no default backgrounds on anything, so it's all transparent.
one side-effect of this change is that u.ctx.canvas.width
is no longer representative of the plotting area, since it includes the axes. this is useful for plugins that want to draw on the canvas directly. you can still technically reverse engineer/parse it from the style
applied to .over
and .under
, but it's not a particularly pleasant exercise. i'll be exposing a u.bbox: {left, top, width, height}
that holds the coords (in canvas pixels) of the plotting area. i'm not too thrilled with vagueness of the property name but couldn't think of anything short that made things more clear: .plot
, .rect
, .area
. willing to evaluate other short suggestions!
another thing i ran into when porting the demos is that it's not possible to invoke a cross-hooks global canvas state to affect the grid but not the axes & labels. e.g. the draw-hooks demo applies a ctx.filter()
to blur the grid.
i'm not particularly concerned with losing this since i think it's super niche. the only use for blur that i can think of drawing a bg image and blurring it, but this can already be done in the drawClear
hook.
i may add a separate drawAxes
hook, since drawGrid now [confusingly] includes drawing the axes. so it may become possible again.
i'll be exposing a u.bbox: {left, top, width, height} that holds the coords (in canvas pixels) of the plotting area.
Thank you for this. I'm currently drawing a second canvas over yours as a second data layer and depend on these coords. "bbox", "bounds", "area", "innerArea" would be easy to understand for me.
ok, the changes have landed. very terse rundown of the new/changed stuff:
drawGrid
hook renamed to drawAxes
u.bbox: coords of plot region, in canvas px
left
top
width
height
opts.axis:
font "12px Roboto"
labelFont "bold 12px Roboto"
gap space between ticks and labels, in css px
tick
show
width
stroke
dash
size length of tick, in css px
Nice work, looks good.
Agree with you regarding the drawGrid
, drawAxis
changes.
Personally, I'd like to see a drawGrid
fire after all axis are drawn. That would makes it much easier to draw above the grid.
Adding drawGrid
would also fix a bug in the draw hooks demo (if you zoom in too far, only a single axis is drawn and the blur is not turned off).
Personally, I'd like to see a
drawGrid
fire after all axis are drawn.
drawAxes
(formerly drawGrid
) already fires after the axes & grid are drawn, so you can draw over the grid at that point (as long as you stay within u.bbox
bounds to not draw on top of the axes). the only loss in functionality here is the inability to apply a global canvas operation like filter
in drawClear
because it will affect both the axes and grid. if you look at how the draw code is structured internally, there's actually no separate passes to draw these things. the grid is drawn based on the axis where it is defined - so, incrementally. i disabled the blur for now. it's interesting, but i'll live without it.
would reduce the need to add scale padding in userland (#71) and improve tick alignment and tick width mis-matches as seen in https://github.com/leeoniya/uPlot/issues/108#issue-556574253.
EDIT: might not be feasible, since there can be multiple axes on a single side, which also need ticks, but would not be adjacent to the canvas. drawing everything on canvas is a bigger undertaking.