Skip to content

Commit

Permalink
add cursor.points.one to use only single dom hover point for all series
Browse files Browse the repository at this point in the history
  • Loading branch information
leeoniya committed Mar 2, 2024
1 parent b7c8eca commit 84a3835
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 189 deletions.
14 changes: 14 additions & 0 deletions bench/uPlot-10M.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@
title: data[0].length.toLocaleString("en-US") + " points x " + (data.length - 1) + " series",
width: 1920,
height: 400,
focus: {
alpha: 1
},
cursor: {
points: {
one: true,
},
focus: {
prox: 30
}
},
// legend: {
// show: false,
// },
scales: {
x: {
time: false
Expand Down
142 changes: 95 additions & 47 deletions dist/uPlot.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1321,6 +1321,7 @@ const cursorOpts = {
lock: false,
move: cursorMove,
points: {
one: false,
show: cursorPointShow,
size: cursorPointSize,
width: 0,
Expand Down Expand Up @@ -3498,25 +3499,24 @@ function uPlot(opts, data, then) {
const focus = self.focus = assign({}, opts.focus || {alpha: 0.3}, cursor.focus);

const cursorFocus = focus.prox >= 0;
const cursorOnePt = cursorFocus && points.one;

// series-intersection markers
let cursorPts = [null];
let cursorPts = [];
// position caches in CSS pixels
let cursorPtsLft = [null];
let cursorPtsTop = [null];
let cursorPtsLft = [];
let cursorPtsTop = [];

function initCursorPt(s, si) {
if (si > 0) {
let pt = cursor.points.show(self, si);
let pt = points.show(self, si);

if (pt) {
addClass(pt, CURSOR_PT);
addClass(pt, s.class);
elTrans(pt, -10, -10, plotWidCss, plotHgtCss);
over.insertBefore(pt, cursorPts[si]);
if (pt) {
addClass(pt, CURSOR_PT);
addClass(pt, s.class);
elTrans(pt, -10, -10, plotWidCss, plotHgtCss);
over.insertBefore(pt, cursorPts[si]);

return pt;
}
return pt;
}
}

Expand All @@ -3529,7 +3529,7 @@ function uPlot(opts, data, then) {
s.label = s.label || (isTime ? timeSeriesLabel : numSeriesLabel);
}

if (i > 0) {
if (cursorOnePt || i > 0) {
s.width = s.width == null ? 1 : s.width;
s.paths = s.paths || linearPath || retNull;
s.fillTo = fnOrSelf(s.fillTo || seriesFillTo);
Expand Down Expand Up @@ -3568,13 +3568,18 @@ function uPlot(opts, data, then) {
if (cursor.show) {
activeIdxs.splice(i, 0, null);

let pt = initCursorPt(s, i);
let pt = null;

if (pt != null) {
cursorPts.splice(i, 0, pt);
cursorPtsLft.splice(i, 0, 0);
cursorPtsTop.splice(i, 0, 0);
if (cursorOnePt) {
if (i == 0)
pt = initCursorPt(s, i);
}
else if (i > 0)
pt = initCursorPt(s, i);

cursorPts.splice(i, 0, pt);
cursorPtsLft.splice(i, 0, 0);
cursorPtsTop.splice(i, 0, 0);
}

fire("addSeries", i);
Expand Down Expand Up @@ -3605,12 +3610,9 @@ function uPlot(opts, data, then) {

if (cursor.show) {
activeIdxs.splice(i, 1);

if (cursorPts.length > 1) {
cursorPts.splice(i, 1)[0].remove();
cursorPtsLft.splice(i, 1);
cursorPtsTop.splice(i, 1);
}
cursorPts.splice(i, 1)[0].remove();
cursorPtsLft.splice(i, 1);
cursorPtsTop.splice(i, 1);
}

// TODO: de-init no-longer-needed scales?
Expand Down Expand Up @@ -4933,7 +4935,7 @@ function uPlot(opts, data, then) {
label && remClass(label, OFF);
else {
label && addClass(label, OFF);
cursorPts.length > 1 && elTrans(cursorPts[i], -10, -10, plotWidCss, plotHgtCss);
elTrans(cursorPts[i], -10, -10, plotWidCss, plotHgtCss);
}
}

Expand Down Expand Up @@ -5197,6 +5199,7 @@ function uPlot(opts, data, then) {
let noDataInRange = i0 > i1; // works for mode 1 only

closestDist = inf;
closestSeries = null;

// TODO: extract
let xDim = scaleX.ori == 0 ? plotWidCss : plotHgtCss;
Expand All @@ -5207,9 +5210,8 @@ function uPlot(opts, data, then) {
idx = cursor.idx = null;

for (let i = 0; i < series.length; i++) {
if (i > 0) {
cursorPts.length > 1 && elTrans(cursorPts[i], -10, -10, plotWidCss, plotHgtCss);
}
let pt = cursorPts[i];
pt != null && elTrans(pt, -10, -10, plotWidCss, plotHgtCss);
}

if (cursorFocus)
Expand All @@ -5232,6 +5234,15 @@ function uPlot(opts, data, then) {
xPos = valToPosX(data[0][idx], scaleX, xDim, 0);
}

// closest pt values
let _ptLft = -10;
let _ptTop = -10;
let _ptWid = 0;
let _ptHgt = 0;
let _centered = true;
let _ptFill = '';
let _ptStroke = '';

for (let i = mode == 2 ? 1 : 0; i < series.length; i++) {
let s = series[i];

Expand Down Expand Up @@ -5282,23 +5293,22 @@ function uPlot(opts, data, then) {
}
}

let hPos, vPos;
if (shouldSetLegend || cursorOnePt) {
let hPos, vPos;

if (scaleX.ori == 0) {
hPos = xPos2;
vPos = yPos;
}
else {
hPos = yPos;
vPos = xPos2;
}

if (shouldSetLegend && cursorPts.length > 1) {
elColor(cursorPts[i], cursor.points.fill(self, i), cursor.points.stroke(self, i));
if (scaleX.ori == 0) {
hPos = xPos2;
vPos = yPos;
}
else {
hPos = yPos;
vPos = xPos2;
}

let ptWid, ptHgt, ptLft, ptTop,
ptStroke, ptFill,
centered = true,
getBBox = cursor.points.bbox;
getBBox = points.bbox;

if (getBBox != null) {
centered = false;
Expand All @@ -5313,19 +5323,57 @@ function uPlot(opts, data, then) {
else {
ptLft = hPos;
ptTop = vPos;
ptWid = ptHgt = cursor.points.size(self, i);
ptWid = ptHgt = points.size(self, i);
}

ptFill = points.fill(self, i);
ptStroke = points.stroke(self, i);

if (cursorOnePt) {
if (i == closestSeries && closestDist <= focus.prox) {
_ptLft = ptLft;
_ptTop = ptTop;
_ptWid = ptWid;
_ptHgt = ptHgt;
_centered = centered;
_ptFill = ptFill;
_ptStroke = ptStroke;
}
}
else {
let pt = cursorPts[i];

elSize(cursorPts[i], ptWid, ptHgt, centered);

cursorPtsLft[i] = ptLft;
cursorPtsTop[i] = ptTop;
cursorPtsLft[i] = ptLft;
cursorPtsTop[i] = ptTop;

elTrans(cursorPts[i], incrRoundUp(ptLft, 1), incrRoundUp(ptTop, 1), plotWidCss, plotHgtCss);
elSize(pt, ptWid, ptHgt, centered);
elColor(pt, ptFill, ptStroke);
elTrans(pt, incrRoundUp(ptLft, 1), incrRoundUp(ptTop, 1), plotWidCss, plotHgtCss);
}
}
}
}

// if only using single hover point (at cursorPts[0])
// we have trigger styling at last visible series (once closestSeries is settled)
if (cursorOnePt) {
// some of this logic is similar to series focus below, since it matches the behavior by design

let p = focus.prox;

let focusChanged = focusedSeries == null ? closestDist <= p : (closestDist > p || closestSeries != focusedSeries);

if (shouldSetLegend || focusChanged) {
let pt = cursorPts[0];

cursorPtsLft[0] = _ptLft;
cursorPtsTop[0] = _ptTop;

elSize(pt, _ptWid, _ptHgt, _centered);
elColor(pt, _ptFill, _ptStroke);
elTrans(pt, incrRoundUp(_ptLft, 1), incrRoundUp(_ptTop, 1), plotWidCss, plotHgtCss);
}
}
}

// nit: cursor.drag.setSelect is assumed always true
Expand Down
2 changes: 2 additions & 0 deletions dist/uPlot.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ declare namespace uPlot {

export interface Points {
show?: Points.Show;
/** only show single y-closest point on hover (only works when cursor.focus.prox >= 0) */
one?: boolean;
/** hover point diameter in CSS pixels */
size?: Points.Size;
/** hover point bbox in CSS pixels (will be used instead of size) */
Expand Down
Loading

0 comments on commit 84a3835

Please sign in to comment.